From 89cc89fa3183372d2dd4bb4424addaf39c02ad2d Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Sat, 16 Jul 2016 00:39:02 -0700 Subject: [PATCH 001/120] updated testlet doc Signed-off-by: Matt Oswalt --- docs/testlets.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/testlets.rst b/docs/testlets.rst index 12a5c39..fab1508 100644 --- a/docs/testlets.rst +++ b/docs/testlets.rst @@ -9,6 +9,17 @@ This allows the user to simply "use" this application, and specify which agents The testlet is actually run by the ToDD agent, so if there are 3 agents participating in a test, then 3 testlets are running. All logic that performs the test should be contained within the +Testrun Definition +------------------ + +When you want to run a certain testlet, you refer to it by name. There are a number of testlets built in to ToDD and are therefore reserved: + +* http +* bandwidth +* ping + +You can, of course, build your own testlet (provided it follows the standard defined on this page) and refer to it by it's filename. + Check Mode ---------- Each testlet must support a "check mode". This is a way of running a testlet that allows the ToDD agent to know whether or not a test can be performed, without actually running the test. From 573f2b4050036a4608f86242d6c403d17614c01c Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Sat, 16 Jul 2016 00:39:14 -0700 Subject: [PATCH 002/120] Added testlet interface Signed-off-by: Matt Oswalt --- agent/testing/testlets/testlets.go | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 agent/testing/testlets/testlets.go diff --git a/agent/testing/testlets/testlets.go b/agent/testing/testlets/testlets.go new file mode 100644 index 0000000..fc84175 --- /dev/null +++ b/agent/testing/testlets/testlets.go @@ -0,0 +1,7 @@ +package testlets + +// Testlet defines what a testlet should look like if built in native +// go and compiled with the agent +type Testlet interface { + Run(string, []string) ([]byte, error) +} From 0a9d723f6e82aa519e2118981f9026ba0961146f Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Sat, 16 Jul 2016 00:32:17 -0700 Subject: [PATCH 003/120] Simplified error returns for client (#45) Signed-off-by: Matt Oswalt --- api/client/create.go | 18 +++++------------- api/client/delete.go | 4 +--- api/client/objects.go | 3 +-- api/client/run.go | 40 ++++++++++++++-------------------------- cmd/todd/main.go | 17 ++++++++++------- 5 files changed, 31 insertions(+), 51 deletions(-) diff --git a/api/client/create.go b/api/client/create.go index 05f469f..33d9627 100644 --- a/api/client/create.go +++ b/api/client/create.go @@ -29,7 +29,6 @@ func (capi ClientApi) Create(conf map[string]string, yamlFileName string) error // Pull YAML from either stdin or from the filename if stdin is empty yamlDef, err := getYAMLDef(yamlFileName) if err != nil { - fmt.Println("Unable to read object definition") return err } @@ -37,8 +36,7 @@ func (capi ClientApi) Create(conf map[string]string, yamlFileName string) error var baseobj objects.BaseObject err = yaml.Unmarshal(yamlDef, &baseobj) if err != nil { - fmt.Println("YAML file not in correct format") - return err + return errors.New("YAML file not in correct format") } // finalobj represents the object being created, regardless of type. @@ -50,16 +48,14 @@ func (capi ClientApi) Create(conf map[string]string, yamlFileName string) error var group_obj objects.GroupObject err = yaml.Unmarshal(yamlDef, &group_obj) if err != nil { - fmt.Println("Group YAML object not in correct format") - return err + return errors.New("Group YAML object not in correct format") } finalobj = group_obj case "testrun": var testrun_obj objects.TestRunObject err = yaml.Unmarshal(yamlDef, &testrun_obj) if err != nil { - fmt.Println("Testrun YAML object not in correct format") - return err + return errors.New("Testrun YAML object not in correct format") } if testrun_obj.Spec.TargetType == "group" { @@ -77,15 +73,13 @@ func (capi ClientApi) Create(conf map[string]string, yamlFileName string) error finalobj = testrun_obj default: - fmt.Println("Invalid object type provided") return errors.New("Invalid object type provided") } // Marshal the final object into JSON json_str, err := json.Marshal(finalobj) if err != nil { - fmt.Println("Problem marshalling the final object into JSON") - return err + return errors.New("Problem marshalling the final object into JSON") } // Construct API request, and send POST to server for this object @@ -110,7 +104,6 @@ func (capi ClientApi) Create(conf map[string]string, yamlFileName string) error if resp.Status == "200 OK" { fmt.Println("[OK]") } else { - fmt.Println(resp.Status) return errors.New(resp.Status) } @@ -126,8 +119,7 @@ func getYAMLDef(yamlFileName string) ([]byte, error) { // Quit if there's nothing on stdin, and there's no arg either if yamlFileName == "" { - fmt.Println("Please provide definition file") - return nil, errors.New("Object definition file not provided") + return nil, errors.New("Object definition file not provided - please provide via filename or stdin") } // Read YAML file diff --git a/api/client/delete.go b/api/client/delete.go index ac78e81..3118212 100644 --- a/api/client/delete.go +++ b/api/client/delete.go @@ -21,8 +21,7 @@ func (capi ClientApi) Delete(conf map[string]string, objType, objLabel string) e // If insufficient subargs were provided, error out if objType == "" || objLabel == "" { - fmt.Println("Error, need to provide type and label (Ex. 'todd delete group datacenter')") - return errors.New("invalid syntax") + return errors.New("Error, need to provide type and label (Ex. 'todd delete group datacenter')") } // anonymous struct to hold our delete info @@ -62,7 +61,6 @@ func (capi ClientApi) Delete(conf map[string]string, objType, objLabel string) e if resp.Status == "200 OK" { fmt.Println("[OK]") } else { - fmt.Println(resp.Status) return errors.New(resp.Status) } diff --git a/api/client/objects.go b/api/client/objects.go index aafa0e6..e71651f 100644 --- a/api/client/objects.go +++ b/api/client/objects.go @@ -25,8 +25,7 @@ func (capi ClientApi) Objects(conf map[string]string, objType string) error { // If no subarg was provided, instruct the user to provide the object type if objType == "" { - fmt.Println("Please provide the object type") - return errors.New("Object type not provided") + return errors.New("Please provide the object type") } url := fmt.Sprintf("http://%s:%s/v1/object/%s", conf["host"], conf["port"], objType) diff --git a/api/client/run.go b/api/client/run.go index 51757a8..a6e2295 100644 --- a/api/client/run.go +++ b/api/client/run.go @@ -30,9 +30,7 @@ func (capi ClientApi) Run(conf map[string]string, testrunName string, displayRep // If no subarg was provided, do nothing if testrunName == "" { - errorMsg := "Please provide testrun object name to run." - fmt.Println(errorMsg) - return errors.New(errorMsg) + return errors.New("Please provide testrun object name to run.") } if !skipConfirm { @@ -84,40 +82,31 @@ func (capi ClientApi) Run(conf map[string]string, testrunName string, displayRep // Print a regular OK message if object was written successfully - else print the HTTP status code if resp.Status != "200 OK" { - fmt.Println(resp.Status) return errors.New(resp.Status) } serverResponse, _ := ioutil.ReadAll(resp.Body) - errorMsg := "" - switch string(serverResponse) { case "notfound": - errorMsg = "ERROR - Specified testrun object not found." + return errors.New("ERROR - Specified testrun object not found.") case "invalidtopology": - errorMsg = "ERROR - Not enough agents are in the groups specified by the testrun" + return errors.New("ERROR - Not enough agents are in the groups specified by the testrun") case "failure": - errorMsg = "ERROR - some kind of error was encountered on the server. Test was not run." - } - - if errorMsg != "" { - fmt.Println(errorMsg) - return errors.New(errorMsg) + return errors.New("ERROR - some kind of error was encountered on the server. Test was not run.") } - fmt.Println("") - testUUID := string(serverResponse) - fmt.Print("RUNNING TEST: ", testUUID) + fmt.Print("\nRUNNING TEST: ", testUUID) fmt.Print("\n\n") fmt.Println("(Please be patient while the test finishes...)") err = listenForTestStatus(conf) if err != nil { - fmt.Println(err, "Will now watch the testrun metrics API for 45 seconds to see if we get a result that way. Please wait...") + fmt.Printf("Problem subscribing to testrun updates stream: %s\n", err) + fmt.Println("Will now watch the testrun metrics API for 45 seconds to see if we get a result that way. Please wait...") } // Poll for results @@ -126,9 +115,7 @@ func (capi ClientApi) Run(conf map[string]string, testrunName string, displayRep for err != nil { select { case <-timeout: - errorMsg := "Failed to retrieve test data after 45 seconds. Something must be wrong - quitting." - fmt.Println(errorMsg) - return errors.New(errorMsg) + return errors.New("Failed to retrieve test data after 45 seconds. Something must be wrong - quitting.") default: time.Sleep(1 * time.Second) data, err = getRunResult(conf, testUUID) @@ -179,10 +166,12 @@ func getRunResult(conf map[string]string, testUUID string) ([]byte, error) { func listenForTestStatus(conf map[string]string) error { retries := 0 conn, err := net.Dial("tcp", fmt.Sprintf("%s:8081", conf["host"])) + + // If the call to net.Dial produces an error, this loop will execute the call again + // until "retries" reaches it's configured limit for err != nil { if retries > 5 { - fmt.Println("Failed to subscribe to test event stream after several retries.") - return errors.New("session failed") + return errors.New("Failed to subscribe to test event stream after several retries.") } retries++ @@ -201,7 +190,7 @@ func listenForTestStatus(conf map[string]string) error { // If an error is raised, it's probably because the server killed the connection if err != nil { // TODO(mierdin): This doesn't really tell us if the connection died because of an error or not - return errors.New("session disconnected") + return errors.New("Disconnected from testrun status stream") } var statuses map[string]string @@ -228,8 +217,7 @@ func listenForTestStatus(conf map[string]string) error { case "finished": finished++ default: - fmt.Println("Invalid status recieved.") - os.Exit(1) + return errors.New("Invalid status received.") } } diff --git a/cmd/todd/main.go b/cmd/todd/main.go index 41e6f0c..2ba282c 100644 --- a/cmd/todd/main.go +++ b/cmd/todd/main.go @@ -62,7 +62,10 @@ func main() { fmt.Println(err) os.Exit(1) } - clientapi.DisplayAgents(agents, !(c.Args().Get(0) == "")) + err = clientapi.DisplayAgents(agents, !(c.Args().Get(0) == "")) + if err != nil { + fmt.Println("Problem displaying agents (client-side)") + } }, }, @@ -80,7 +83,7 @@ func main() { c.Args().Get(0), ) if err != nil { - fmt.Println("Unable to create object on ToDD server.") + fmt.Println(err) os.Exit(1) } }, @@ -100,7 +103,8 @@ func main() { c.Args().Get(1), ) if err != nil { - fmt.Println("ERROR - Are you sure you provided the right object type and/or label?") + fmt.Printf("ERROR: %s\n", err) + fmt.Println("(Are you sure you provided the right object type and/or label?)") os.Exit(1) } }, @@ -118,7 +122,7 @@ func main() { }, ) if err != nil { - fmt.Println("ERROR - Unable to obtain current group mapping") + fmt.Println(err) os.Exit(1) } }, @@ -137,7 +141,7 @@ func main() { c.Args().Get(0), ) if err != nil { - fmt.Println("ERROR - Unable to retrieve object") + fmt.Println(err) os.Exit(1) } }, @@ -183,10 +187,9 @@ func main() { c.Bool("y"), ) if err != nil { - fmt.Println("ERROR - Problem running testrun") + fmt.Println(err) os.Exit(1) } - }, }, } From ebf0001c5442edb3cbe30ef80fca33a358b5dbcb Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Tue, 19 Jul 2016 01:35:36 -0700 Subject: [PATCH 004/120] Saving progress on building native testlet infrastructure Signed-off-by: Matt Oswalt --- agent/tasks/executetestrun.go | 117 +++++++++++++++---------- agent/tasks/installtestrun.go | 56 +++++++----- agent/testing/testlets/{ping => _ping} | 0 agent/testing/testlets/ping/ping.go | 46 ++++++++++ agent/testing/testlets/testlets.go | 74 +++++++++++++++- cmd/todd-agent/main.go | 6 +- 6 files changed, 229 insertions(+), 70 deletions(-) rename agent/testing/testlets/{ping => _ping} (100%) create mode 100644 agent/testing/testlets/ping/ping.go diff --git a/agent/tasks/executetestrun.go b/agent/tasks/executetestrun.go index 6ed6e48..09904b0 100644 --- a/agent/tasks/executetestrun.go +++ b/agent/tasks/executetestrun.go @@ -21,9 +21,20 @@ import ( log "github.com/Sirupsen/logrus" "github.com/Mierdin/todd/agent/cache" + "github.com/Mierdin/todd/agent/testing/testlets" "github.com/Mierdin/todd/config" ) +var ( + // gatheredData represents test data from this agent for all targets. + // Key is target name, value is JSON output from testlet for that target + // This is reset to a blank map every time ExecuteTestRunTask is called + gatheredData = make(map[string]string) + + // Use a wait group to ensure that all of the testlets have a chance to finish + wg sync.WaitGroup +) + // ExecuteTestRunTask defines this particular task. type ExecuteTestRunTask struct { BaseTask @@ -37,6 +48,9 @@ type ExecuteTestRunTask struct { // a testrun will be executed once per target, all in parallel. func (ett ExecuteTestRunTask) Run() error { + // Make sure we're working with a clean slate + gatheredData = map[string]string{} + // Waiting three seconds to ensure all the agents have their tasks before we potentially hammer the network // TODO(mierdin): This is a bit of a copout. I would like to do something a little more robust than simply waiting // for a few seconds in the future. @@ -50,70 +64,79 @@ func (ett ExecuteTestRunTask) Run() error { return errors.New("Problem retrieving testrun from agent cache") } - // Generate path to testlet and make sure it exists. - testlet_path := fmt.Sprintf("%s/assets/testlets/%s", ett.Config.LocalResources.OptDir, tr.Testlet) - if _, err := os.Stat(testlet_path); os.IsNotExist(err) { - log.Errorf("Testlet %s does not exist on this agent", tr.Testlet) - return errors.New("Error executing testrun - testlet doesn't exist on this agent.") - } - log.Debugf("IMMA FIRIN MAH LAZER (for test %s) ", ett.TestUuid) - // Use a wait group to ensure that all of the testlets have a chance to finish - var wg sync.WaitGroup + // Specify size of wait group wg.Add(len(tr.Targets)) - // gatheredData represents test data from this agent for all targets. - // Key is target name, value is JSON output from testlet for that target - gatheredData := make(map[string]string) + // Determine if this is a native testlet + nativeTestlet, err := testlets.NewTestlet(tr.Testlet) + native := (err == nil) // Execute testlets against all targets asynchronously for i := range tr.Targets { thisTarget := tr.Targets[i] - go func() { - defer wg.Done() - - log.Debugf("Full testlet command and args: '%s %s %s'", testlet_path, thisTarget, tr.Args) - cmd := exec.Command(testlet_path, thisTarget, tr.Args) - - // Stdout buffer - cmdOutput := &bytes.Buffer{} - // Attach buffer to command - cmd.Stdout = cmdOutput + if native { + go func() { - // Execute collector - cmd.Start() + // TODO(mierdin): Just calling a temporary method to ensure we can get to the native testlet. + // (it works!) + log.Error(nativeTestlet.Test()) + }() + } else { + // Generate path to testlet and make sure it exists. + testlet_path := fmt.Sprintf("%s/assets/testlets/%s", ett.Config.LocalResources.OptDir, tr.Testlet) + if _, err := os.Stat(testlet_path); os.IsNotExist(err) { + log.Errorf("Testlet %s does not exist on this agent", tr.Testlet) + return errors.New("Error executing testrun - testlet doesn't exist on this agent.") + } - done := make(chan error, 1) go func() { - done <- cmd.Wait() - }() - // This select statement will block until one of these two conditions are met: - // - The testlet finishes, in which case the channel "done" will be receive a value - // - The configured time limit is exceeded (expected for testlets running in server mode) - select { - case <-time.After(time.Duration(ett.TimeLimit) * time.Second): - if err := cmd.Process.Kill(); err != nil { - log.Errorf("Failed to kill %s after timeout: %s", testlet_path, err) - } else { - log.Debug("Successfully killed ", testlet_path) - } - case err := <-done: - if err != nil { - log.Errorf("Testlet %s completed with error '%s'", testlet_path, err) - gatheredData[thisTarget] = "error" - } else { - log.Debugf("Testlet %s completed without error", testlet_path) + defer wg.Done() + + log.Debugf("Full testlet command and args: '%s %s %s'", testlet_path, thisTarget, tr.Args) + cmd := exec.Command(testlet_path, thisTarget, tr.Args) + + // Stdout buffer + cmdOutput := &bytes.Buffer{} + // Attach buffer to command + cmd.Stdout = cmdOutput + + // Execute collector + cmd.Start() + + done := make(chan error, 1) + go func() { + done <- cmd.Wait() + }() + + // This select statement will block until one of these two conditions are met: + // - The testlet finishes, in which case the channel "done" will be receive a value + // - The configured time limit is exceeded (expected for testlets running in server mode) + select { + case <-time.After(time.Duration(ett.TimeLimit) * time.Second): + if err := cmd.Process.Kill(); err != nil { + log.Errorf("Failed to kill %s after timeout: %s", testlet_path, err) + } else { + log.Debug("Successfully killed ", testlet_path) + } + case err := <-done: + if err != nil { + log.Errorf("Testlet %s completed with error '%s'", testlet_path, err) + gatheredData[thisTarget] = "error" + } else { + log.Debugf("Testlet %s completed without error", testlet_path) + } } - } - // Record test data - gatheredData[thisTarget] = string(cmdOutput.Bytes()) + // Record test data + gatheredData[thisTarget] = string(cmdOutput.Bytes()) - }() + }() + } } wg.Wait() diff --git a/agent/tasks/installtestrun.go b/agent/tasks/installtestrun.go index c45da42..131df4d 100644 --- a/agent/tasks/installtestrun.go +++ b/agent/tasks/installtestrun.go @@ -20,6 +20,7 @@ import ( "github.com/Mierdin/todd/agent/cache" "github.com/Mierdin/todd/agent/defs" + "github.com/Mierdin/todd/agent/testing/testlets" "github.com/Mierdin/todd/config" ) @@ -42,35 +43,48 @@ func (itt InstallTestRunTask) Run() error { return errors.New("Testlet parameter for this testrun is null") } - // Generate path to testlet and make sure it exists. - testlet_path := fmt.Sprintf("%s/assets/testlets/%s", itt.Config.LocalResources.OptDir, itt.Tr.Testlet) - if _, err := os.Stat(testlet_path); os.IsNotExist(err) { - log.Errorf("Testlet %s does not exist on this agent", itt.Tr.Testlet) - return errors.New("Error installing testrun - testlet doesn't exist on this agent.") - } + // Determine if this is a valid native testlet + _, err := testlets.NewTestlet(itt.Tr.Testlet) + + // Not a native testlet - attempt to run check mode on testlet in filesystem + if err != nil { + + // Generate path to testlet and make sure it exists. + testlet_path := fmt.Sprintf("%s/assets/testlets/%s", itt.Config.LocalResources.OptDir, itt.Tr.Testlet) + if _, err := os.Stat(testlet_path); os.IsNotExist(err) { + log.Errorf("Testlet %s does not exist on this agent", itt.Tr.Testlet) + return errors.New("Error installing testrun - testlet doesn't exist on this agent.") + } - // Run the testlet in check mode to verify that everything is okay to run this test - log.Debug("Running testlet in check mode: ", testlet_path) - cmd := exec.Command(testlet_path, "check") + // Run the testlet in check mode to verify that everything is okay to run this test + log.Debug("Running testlet in check mode: ", testlet_path) + cmd := exec.Command(testlet_path, "check") - // Stdout buffer - cmdOutput := &bytes.Buffer{} - // Attach buffer to command - cmd.Stdout = cmdOutput - // Execute collector - cmd.Run() + // Stdout buffer + cmdOutput := &bytes.Buffer{} + // Attach buffer to command + cmd.Stdout = cmdOutput + // Execute collector + cmd.Run() + + // This is probably the best cross-platform way to see if check mode passed. + if strings.Contains(string(cmdOutput.Bytes()), "Check mode PASSED") { + log.Debugf("Check mode for %s passed", testlet_path) + } else { + log.Error("Testlet returned an error during check mode: ", string(cmdOutput.Bytes())) + return errors.New("Testlet returned an error during check mode") + } - // This is probably the best cross-platform way to see if check mode passed. - if strings.Contains(string(cmdOutput.Bytes()), "Check mode PASSED") { - log.Debugf("Check mode for %s passed", testlet_path) } else { - log.Error("Testlet returned an error during check mode: ", string(cmdOutput.Bytes())) - return errors.New("Testlet returned an error during check mode") + + // Nothing to do, as we're using a native testlet + log.Infof("%s is a native testlet - installing testrun.", itt.Tr.Testlet) + } // Insert testrun into agent cache var ac = cache.NewAgentCache(itt.Config) - err := ac.InsertTestRun(itt.Tr) + err = ac.InsertTestRun(itt.Tr) if err != nil { log.Error(err) return errors.New("Problem installing test run into agent cache") diff --git a/agent/testing/testlets/ping b/agent/testing/testlets/_ping similarity index 100% rename from agent/testing/testlets/ping rename to agent/testing/testlets/_ping diff --git a/agent/testing/testlets/ping/ping.go b/agent/testing/testlets/ping/ping.go new file mode 100644 index 0000000..f68b43b --- /dev/null +++ b/agent/testing/testlets/ping/ping.go @@ -0,0 +1,46 @@ +package toddping + +// NOTE ////////////////// +// +// This is a built-in testlet. Currently, the approach is to have each +// testlet under it's own package, which is explicitly imported under +// the ToDD agent's 'main' package. +// +// Currently, each testlet is stored within the ToDD repo in order to +// vet out the architecture, which means they are awkwardly placed +// underneath the "testlets" directory together. However, this should be +// a tempoarary holding place, as the main effort around native testlets +// is being implemented so they can be broken out into their own repos. + +import ( + "github.com/Mierdin/todd/agent/testing/testlets" +) + +type PingTestlet struct{} + +func init() { + testlets.Register("ping", &PingTestlet{}) +} + +// TODO(mierdin): Maybe consider running these asyc by default? Basically +// the "Run" function kicks back a channel of type map[string]string so when +// it's populated, it contains the metrics and you know it can stop + +func (p PingTestlet) Run(target string, args []string) (map[string]string, error) { + + // TODO(mierdin): Implement ping test here + + return map[string]string{}, nil +} + +func (p PingTestlet) Kill() error { + // TODO (mierdin): This will have to be coordinated with the task above. Basically + // you need a way to kill this testlet (and that's really only possible when running + // async) + + return nil +} + +func (p PingTestlet) Test() string { + return "trolololol" +} diff --git a/agent/testing/testlets/testlets.go b/agent/testing/testlets/testlets.go index fc84175..6ad65cf 100644 --- a/agent/testing/testlets/testlets.go +++ b/agent/testing/testlets/testlets.go @@ -1,7 +1,79 @@ package testlets +import ( + "errors" + "fmt" + "sort" + "sync" + //"sync/atomic" +) + +var ( + testletsMu sync.RWMutex + testlets = make(map[string]Testlet) +) + // Testlet defines what a testlet should look like if built in native // go and compiled with the agent type Testlet interface { - Run(string, []string) ([]byte, error) + + // Params are + // target (string) + // args ([]string) + // + // Returns: + // metrics (map[string]interface{}) + // (name of metric is key, value is metric value) + Run(string, []string) (map[string]string, error) + + Kill() error + + Test() string //TODO(mierdin): Remove me +} + +//NewTestlet produces a new testlet based on the "name" param +func NewTestlet(name string) (Testlet, error) { + + if testlet, ok := testlets[name]; ok { + return testlet, nil + } else { + return nil, errors.New( + fmt.Sprintf("'%s' not currently supported as a native testlet"), + ) + } +} + +// Register makes a testlet available by the provided name. +// If Register is called twice with the same name or if testlet is nil, +// it will return an error +func Register(name string, testlet Testlet) error { + testletsMu.Lock() + defer testletsMu.Unlock() + if testlet == nil { + return errors.New("Register testlet is nil") + } + if _, dup := testlets[name]; dup { + return errors.New("Register called twice for testlet " + name) + } + testlets[name] = testlet + return nil +} + +func unregisterAllDrivers() { + testletsMu.Lock() + defer testletsMu.Unlock() + // For tests. + testlets = make(map[string]Testlet) +} + +// Testlets returns a sorted list of the names of the registered testlets. +func Testlets() []string { + testletsMu.RLock() + defer testletsMu.RUnlock() + var list []string + for name := range testlets { + list = append(list, name) + } + sort.Strings(list) + return list } diff --git a/cmd/todd-agent/main.go b/cmd/todd-agent/main.go index 51f75ad..30950d9 100644 --- a/cmd/todd-agent/main.go +++ b/cmd/todd-agent/main.go @@ -14,6 +14,8 @@ import ( "os" "time" + log "github.com/Sirupsen/logrus" + "github.com/Mierdin/todd/agent/cache" "github.com/Mierdin/todd/agent/defs" "github.com/Mierdin/todd/agent/facts" @@ -21,7 +23,9 @@ import ( "github.com/Mierdin/todd/comms" "github.com/Mierdin/todd/config" "github.com/Mierdin/todd/hostresources" - log "github.com/Sirupsen/logrus" + + // Testlet imports (importing these packages registers the testlets) + _ "github.com/Mierdin/todd/agent/testing/testlets/ping" ) // Command-line Arguments From 5742306fec52bd4df8c2ce1e4090ca6b45e1e769 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Tue, 19 Jul 2016 01:36:05 -0700 Subject: [PATCH 005/120] Docs update Signed-off-by: Matt Oswalt --- docs/nativetests.rst | 6 ++++++ docs/testlets.rst | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 docs/nativetests.rst diff --git a/docs/nativetests.rst b/docs/nativetests.rst new file mode 100644 index 0000000..e6c04a3 --- /dev/null +++ b/docs/nativetests.rst @@ -0,0 +1,6 @@ +Native Tests +================================ + +Need to talk about the native tests you've built in, and turn the "testlets" doc into more of a "so you want to build your own, eh?" + +Also need to figure out if you want to refer to both native and non-native as "testlets", or maybe reserve that for non-native \ No newline at end of file diff --git a/docs/testlets.rst b/docs/testlets.rst index fab1508..06afe4d 100644 --- a/docs/testlets.rst +++ b/docs/testlets.rst @@ -18,7 +18,7 @@ When you want to run a certain testlet, you refer to it by name. There are a num * bandwidth * ping -You can, of course, build your own testlet (provided it follows the standard defined on this page) and refer to it by it's filename. +If you don't want to use any of the built-in testlets, you can, of course, build your own testlet (provided it follows the standard defined on this page) and refer to it by it's filename. Check Mode ---------- From 726bc832316b420874b1f62c1ff2592bff4bc55a Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Thu, 28 Jul 2016 01:45:12 -0700 Subject: [PATCH 006/120] Made progress on a native testlet implementation Signed-off-by: Matt Oswalt --- agent/tasks/executetestrun.go | 33 ++++++++++++++++++++++------- agent/testing/testlets/ping/ping.go | 21 +++++++++--------- agent/testing/testlets/testlets.go | 10 ++++++++- 3 files changed, 45 insertions(+), 19 deletions(-) diff --git a/agent/tasks/executetestrun.go b/agent/tasks/executetestrun.go index 09904b0..c72ccb1 100644 --- a/agent/tasks/executetestrun.go +++ b/agent/tasks/executetestrun.go @@ -69,21 +69,38 @@ func (ett ExecuteTestRunTask) Run() error { // Specify size of wait group wg.Add(len(tr.Targets)) - // Determine if this is a native testlet - nativeTestlet, err := testlets.NewTestlet(tr.Testlet) - native := (err == nil) - // Execute testlets against all targets asynchronously for i := range tr.Targets { thisTarget := tr.Targets[i] - if native { + if testlets.IsNativeTestlet(tr.Testlet) { go func() { - // TODO(mierdin): Just calling a temporary method to ensure we can get to the native testlet. - // (it works!) - log.Error(nativeTestlet.Test()) + // TODO(mierdin): Something worried me here (can't remember what) regarding + // if only some agents were running native testlets, does this wait group methodology work? + // Sorry it's not clear, I have had a bit too much wine. + defer wg.Done() + + nativeTestlet, err := testlets.NewTestlet(tr.Testlet) + if err != nil { + //TODO(mierdin) do something + } + + metrics, err := nativeTestlet.Run("8.8.8.8", []string{"-c 10", "-s"}) + if err != nil { + //TODO(mierdin) do something + } + + // The metrics infrastructure requires that we collect metrics as a JSON string + // (which is a result of building non-native testlets in early versions of ToDD) + // So let's convert, and add to gatheredData + metrics_json, err := json.Marshal(metrics) + if err != nil { + //TODO(mierdin) do something + } + gatheredData[thisTarget] = string(metrics_json) + }() } else { // Generate path to testlet and make sure it exists. diff --git a/agent/testing/testlets/ping/ping.go b/agent/testing/testlets/ping/ping.go index f68b43b..51f29e6 100644 --- a/agent/testing/testlets/ping/ping.go +++ b/agent/testing/testlets/ping/ping.go @@ -13,24 +13,29 @@ package toddping // is being implemented so they can be broken out into their own repos. import ( + "time" + "github.com/Mierdin/todd/agent/testing/testlets" ) type PingTestlet struct{} func init() { + + // This is important - register the name of this testlet + // (the name the user will use in a testrun definition) testlets.Register("ping", &PingTestlet{}) } -// TODO(mierdin): Maybe consider running these asyc by default? Basically -// the "Run" function kicks back a channel of type map[string]string so when -// it's populated, it contains the metrics and you know it can stop - func (p PingTestlet) Run(target string, args []string) (map[string]string, error) { - // TODO(mierdin): Implement ping test here + // TODO(mierdin): Implement ping test here - this is just a mock + time.Sleep(3000 * time.Millisecond) + return map[string]string{ + "avg_latency_ms": "25.144", + "packet_loss_percentage": "0", + }, nil - return map[string]string{}, nil } func (p PingTestlet) Kill() error { @@ -40,7 +45,3 @@ func (p PingTestlet) Kill() error { return nil } - -func (p PingTestlet) Test() string { - return "trolololol" -} diff --git a/agent/testing/testlets/testlets.go b/agent/testing/testlets/testlets.go index 6ad65cf..64ed586 100644 --- a/agent/testing/testlets/testlets.go +++ b/agent/testing/testlets/testlets.go @@ -27,8 +27,16 @@ type Testlet interface { Run(string, []string) (map[string]string, error) Kill() error +} - Test() string //TODO(mierdin): Remove me +// IsNativeTestlet polls the list of registered native testlets, and returns +// true if the referenced name exists +func IsNativeTestlet(name string) bool { + if _, ok := testlets[name]; ok { + return true + } else { + return false + } } //NewTestlet produces a new testlet based on the "name" param From 2f389b67f9c1fb59813bc56eb6e7adb98ab2978c Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Fri, 29 Jul 2016 02:21:01 -0700 Subject: [PATCH 007/120] Implement async handling of testlet running Signed-off-by: Matt Oswalt --- agent/tasks/executetestrun.go | 7 ++- agent/testing/testlets/ping/ping.go | 29 ++++++----- agent/testing/testlets/testlets.go | 81 ++++++++++++++++++++++++++++- 3 files changed, 101 insertions(+), 16 deletions(-) diff --git a/agent/tasks/executetestrun.go b/agent/tasks/executetestrun.go index c72ccb1..b9210de 100644 --- a/agent/tasks/executetestrun.go +++ b/agent/tasks/executetestrun.go @@ -87,9 +87,11 @@ func (ett ExecuteTestRunTask) Run() error { //TODO(mierdin) do something } - metrics, err := nativeTestlet.Run("8.8.8.8", []string{"-c 10", "-s"}) + metrics, err := nativeTestlet.Run("8.8.8.8", []string{"-c 10", "-s"}, ett.TimeLimit) + //log.Error(nativeTestlet.RunFunction) if err != nil { - //TODO(mierdin) do something + log.Errorf("Testlet completed with error '%s'", err) + gatheredData[thisTarget] = "error" } // The metrics infrastructure requires that we collect metrics as a JSON string @@ -125,6 +127,7 @@ func (ett ExecuteTestRunTask) Run() error { // Execute collector cmd.Start() + // TODO(mierdin): Does this need to be a buffered channel? done := make(chan error, 1) go func() { done <- cmd.Wait() diff --git a/agent/testing/testlets/ping/ping.go b/agent/testing/testlets/ping/ping.go index 51f29e6..8ceb887 100644 --- a/agent/testing/testlets/ping/ping.go +++ b/agent/testing/testlets/ping/ping.go @@ -9,25 +9,38 @@ package toddping // Currently, each testlet is stored within the ToDD repo in order to // vet out the architecture, which means they are awkwardly placed // underneath the "testlets" directory together. However, this should be -// a tempoarary holding place, as the main effort around native testlets +// a temporary holding place, as the main effort around native testlets // is being implemented so they can be broken out into their own repos. import ( "time" + // log "github.com/Sirupsen/logrus" + "github.com/Mierdin/todd/agent/testing/testlets" ) -type PingTestlet struct{} +type PingTestlet struct { + testlets.BaseTestlet +} func init() { + var pt = PingTestlet{} + + // Ensure the RunFunction attribute is set correctly. + // This allows the underlying testlet infrastructure + // to know what function to call at runtime + pt.RunFunction = pt.RunTestlet + // This is important - register the name of this testlet // (the name the user will use in a testrun definition) - testlets.Register("ping", &PingTestlet{}) + testlets.Register("ping", &pt) } -func (p PingTestlet) Run(target string, args []string) (map[string]string, error) { +// RunTestlet implements the core logic of the testlet. Don't worry about running asynchronously, +// that's handled by the infrastructure. +func (p PingTestlet) RunTestlet(target string, args []string) (map[string]string, error) { // TODO(mierdin): Implement ping test here - this is just a mock time.Sleep(3000 * time.Millisecond) @@ -37,11 +50,3 @@ func (p PingTestlet) Run(target string, args []string) (map[string]string, error }, nil } - -func (p PingTestlet) Kill() error { - // TODO (mierdin): This will have to be coordinated with the task above. Basically - // you need a way to kill this testlet (and that's really only possible when running - // async) - - return nil -} diff --git a/agent/testing/testlets/testlets.go b/agent/testing/testlets/testlets.go index 64ed586..0ebb0c7 100644 --- a/agent/testing/testlets/testlets.go +++ b/agent/testing/testlets/testlets.go @@ -5,30 +5,104 @@ import ( "fmt" "sort" "sync" + "time" //"sync/atomic" + + log "github.com/Sirupsen/logrus" ) var ( testletsMu sync.RWMutex testlets = make(map[string]Testlet) + done = make(chan error) ) // Testlet defines what a testlet should look like if built in native // go and compiled with the agent type Testlet interface { + // Run is the "workflow" function for a testlet. It handles running + // the RunTestlet function asynchronously and managing the state therein. + // // Params are // target (string) // args ([]string) + // timeLimit (int in seconds) // // Returns: // metrics (map[string]interface{}) // (name of metric is key, value is metric value) - Run(string, []string) (map[string]string, error) + // + // Keep as much logic out of here as possible. All native testlets + // must support a "Kill" method, so it's best to implement core testlet + // logic in a separate function so that the Run and Kill commands can manage + // execution of that logic in a goroutine + Run(string, []string, int) (map[string]string, error) + // RunTestlet is designed to be the one-stop shop for testlet logic. + // The developer of a native testlet just needs to implement the testlet logic here, + // without worrying about things like managing goroutines or channels. That's all + // managed by the "Run" or "Kill" functions + RunTestlet(string, []string) (map[string]string, error) + // TODO(mierdin): is this really the best name for it? Maybe something that's less confusing, less like "Run" + + // All testlets must be able to stop operation when sent a Kill command. Kill() error } +type rtfunc func(target string, args []string) (map[string]string, error) + +type BaseTestlet struct { + + // rtfunc is a type that will store our RunTestlet function. It is the responsibility + // of the "child" testlet to set this value upon creation + RunFunction rtfunc +} + +// Run takes care of running the testlet function and managing it's operation given the parameters provided +func (b BaseTestlet) Run(target string, args []string, timeLimit int) (map[string]string, error) { + + var metrics map[string]string + + // TODO(mierdin): ensure channel is nil + // done = make(chan error) + + // TODO(mierdin): Based on experimentation, this will keep running even if this function returns. + // Need to be sure about how this ends. Also might want to evaluate the same for the existing non-native model, likely has the same issue + go func() { + theseMetrics, err := b.RunFunction(target, args) + metrics = theseMetrics //TODO(mierdin): Gross. + done <- err + }() + + // This select statement will block until one of these two conditions are met: + // - The testlet finishes, in which case the channel "done" will be receive a value + // - The configured time limit is exceeded (expected for testlets running in server mode) + select { + case <-time.After(time.Duration(timeLimit) * time.Second): + log.Debug("Successfully killed ") + return map[string]string{}, nil + + case err := <-done: + if err != nil { + return map[string]string{}, errors.New("testlet error") // TODO(mierdin): elaborate? + } else { + log.Debugf("Testlet completed without error") + return metrics, nil + } + } +} + +func (b BaseTestlet) Kill() error { + // TODO (mierdin): This will have to be coordinated with the task above. Basically + // you need a way to kill this testlet (and that's really only possible when running + // async) + + // Probably just want to set the channel to something so the select within "Run" will execute + + return nil +} + // IsNativeTestlet polls the list of registered native testlets, and returns // true if the referenced name exists func IsNativeTestlet(name string) bool { @@ -43,6 +117,9 @@ func IsNativeTestlet(name string) bool { func NewTestlet(name string) (Testlet, error) { if testlet, ok := testlets[name]; ok { + + // testlet.runFunction = testlet.run + return testlet, nil } else { return nil, errors.New( @@ -67,7 +144,7 @@ func Register(name string, testlet Testlet) error { return nil } -func unregisterAllDrivers() { +func unregisterAllTestlets() { testletsMu.Lock() defer testletsMu.Unlock() // For tests. From 91ce8ed14f58f854071e4f05b349f130425e7e00 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Sun, 31 Jul 2016 16:30:10 -0700 Subject: [PATCH 008/120] Further progress Signed-off-by: Matt Oswalt --- agent/testing/testlets/ping/ping.go | 57 ++++++++++++++++++++++++++--- agent/testing/testlets/testlets.go | 10 +++-- 2 files changed, 57 insertions(+), 10 deletions(-) diff --git a/agent/testing/testlets/ping/ping.go b/agent/testing/testlets/ping/ping.go index 8ceb887..42cc8d3 100644 --- a/agent/testing/testlets/ping/ping.go +++ b/agent/testing/testlets/ping/ping.go @@ -13,9 +13,10 @@ package toddping // is being implemented so they can be broken out into their own repos. import ( + "fmt" "time" - // log "github.com/Sirupsen/logrus" + log "github.com/Sirupsen/logrus" "github.com/Mierdin/todd/agent/testing/testlets" ) @@ -40,13 +41,57 @@ func init() { // RunTestlet implements the core logic of the testlet. Don't worry about running asynchronously, // that's handled by the infrastructure. -func (p PingTestlet) RunTestlet(target string, args []string) (map[string]string, error) { +func (p PingTestlet) RunTestlet(target string, args []string, kill chan (bool)) (map[string]string, error) { + + // Get number of pings + count := 3 //TODO(mierdin): need to parse from 'args', or if omitted, use a default value + + log.Error(args) + + var latencies []float32 + var replies int + + // Execute ping once per count + i := 0 + for i < count { + select { + case <-kill: + // Terminating early; return empty metrics + return map[string]string{}, nil + default: + + //log.Debugf("Executing ping #%d", i) + + // Mocked ping logic + latency, replyReceived := pingTemp(count) + + latencies = append(latencies, latency) + + if replyReceived { + replies += 1 + } + + i += 1 + time.Sleep(1000 * time.Millisecond) + + } + } + + // Calculate metrics + var latencyTotal float32 = 0 + for _, value := range latencies { + latencyTotal += value + } + avg_latency_ms := latencyTotal / float32(len(latencies)) + packet_loss := (float32(count) - float32(replies)) / float32(count) - // TODO(mierdin): Implement ping test here - this is just a mock - time.Sleep(3000 * time.Millisecond) return map[string]string{ - "avg_latency_ms": "25.144", - "packet_loss_percentage": "0", + "avg_latency_ms": fmt.Sprintf("%.2f", avg_latency_ms), + "packet_loss": fmt.Sprintf("%.2f", packet_loss), }, nil } + +func pingTemp(count int) (float32, bool) { + return float32(count) * 4.234, true +} diff --git a/agent/testing/testlets/testlets.go b/agent/testing/testlets/testlets.go index 0ebb0c7..4458892 100644 --- a/agent/testing/testlets/testlets.go +++ b/agent/testing/testlets/testlets.go @@ -14,7 +14,8 @@ import ( var ( testletsMu sync.RWMutex testlets = make(map[string]Testlet) - done = make(chan error) + done = make(chan error) // Used from within the goroutine to inform the infrastructure it has finished + kill = make(chan bool) // Used from outside the goroutine to inform the goroutine to stop ) // Testlet defines what a testlet should look like if built in native @@ -43,14 +44,14 @@ type Testlet interface { // The developer of a native testlet just needs to implement the testlet logic here, // without worrying about things like managing goroutines or channels. That's all // managed by the "Run" or "Kill" functions - RunTestlet(string, []string) (map[string]string, error) + RunTestlet(string, []string, chan bool) (map[string]string, error) // TODO(mierdin): is this really the best name for it? Maybe something that's less confusing, less like "Run" // All testlets must be able to stop operation when sent a Kill command. Kill() error } -type rtfunc func(target string, args []string) (map[string]string, error) +type rtfunc func(target string, args []string, kill chan bool) (map[string]string, error) type BaseTestlet struct { @@ -66,11 +67,12 @@ func (b BaseTestlet) Run(target string, args []string, timeLimit int) (map[strin // TODO(mierdin): ensure channel is nil // done = make(chan error) + // kill = make(chan bool) // TODO(mierdin): Based on experimentation, this will keep running even if this function returns. // Need to be sure about how this ends. Also might want to evaluate the same for the existing non-native model, likely has the same issue go func() { - theseMetrics, err := b.RunFunction(target, args) + theseMetrics, err := b.RunFunction(target, args, kill) metrics = theseMetrics //TODO(mierdin): Gross. done <- err }() From f023d318690b8db38a6943fcd51f8044d332a4ce Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Mon, 1 Aug 2016 18:50:16 -0700 Subject: [PATCH 009/120] Catch-up commit Signed-off-by: Matt Oswalt --- .gitignore | 3 +- Makefile | 3 +- agent/testing/{testlets => bashtestlets}/http | 0 .../testing/{testlets => bashtestlets}/iperf | 0 .../_ping => bashtestlets/notatallping} | 0 agent/testing/testlets/ping/ping.go | 97 ------------------- assets/assets_unpack.go | 66 ++++++------- cmd/todd-agent/main.go | 2 +- cmd/todd-server/assets.go | 2 +- docs/nativetests.rst | 16 ++- scripts/buildtestlets.sh | 69 +++++++++++++ scripts/start-containers.sh | 2 +- 12 files changed, 124 insertions(+), 136 deletions(-) rename agent/testing/{testlets => bashtestlets}/http (100%) rename agent/testing/{testlets => bashtestlets}/iperf (100%) rename agent/testing/{testlets/_ping => bashtestlets/notatallping} (100%) delete mode 100644 agent/testing/testlets/ping/ping.go create mode 100755 scripts/buildtestlets.sh diff --git a/.gitignore b/.gitignore index 7c8cfbd..9896fa8 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ docs/_build/ preso_secret/ client/repeattest.sh *.out -*.DS_Store* \ No newline at end of file +*.DS_Store* +agent/testing/downloaded_testlets/ \ No newline at end of file diff --git a/Makefile b/Makefile index a3e8efa..8935ba5 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,7 @@ build: docker build -t mierdin/todd -f Dockerfile . compile: + ./scripts/buildtestlets.sh go install ./cmd/... install: configureenv @@ -27,7 +28,7 @@ update_deps: update_assets: go get -u github.com/jteeuwen/go-bindata/... - go-bindata -o assets/assets_unpack.go -pkg="assets" -prefix="agent" agent/testing/testlets/... agent/facts/collectors/... + go-bindata -o assets/assets_unpack.go -pkg="assets" -prefix="agent" agent/testing/bashtestlets/... agent/facts/collectors/... start: compile start-containers.sh 3 /etc/todd/server-int.cfg /etc/todd/agent-int.cfg diff --git a/agent/testing/testlets/http b/agent/testing/bashtestlets/http similarity index 100% rename from agent/testing/testlets/http rename to agent/testing/bashtestlets/http diff --git a/agent/testing/testlets/iperf b/agent/testing/bashtestlets/iperf similarity index 100% rename from agent/testing/testlets/iperf rename to agent/testing/bashtestlets/iperf diff --git a/agent/testing/testlets/_ping b/agent/testing/bashtestlets/notatallping similarity index 100% rename from agent/testing/testlets/_ping rename to agent/testing/bashtestlets/notatallping diff --git a/agent/testing/testlets/ping/ping.go b/agent/testing/testlets/ping/ping.go deleted file mode 100644 index 42cc8d3..0000000 --- a/agent/testing/testlets/ping/ping.go +++ /dev/null @@ -1,97 +0,0 @@ -package toddping - -// NOTE ////////////////// -// -// This is a built-in testlet. Currently, the approach is to have each -// testlet under it's own package, which is explicitly imported under -// the ToDD agent's 'main' package. -// -// Currently, each testlet is stored within the ToDD repo in order to -// vet out the architecture, which means they are awkwardly placed -// underneath the "testlets" directory together. However, this should be -// a temporary holding place, as the main effort around native testlets -// is being implemented so they can be broken out into their own repos. - -import ( - "fmt" - "time" - - log "github.com/Sirupsen/logrus" - - "github.com/Mierdin/todd/agent/testing/testlets" -) - -type PingTestlet struct { - testlets.BaseTestlet -} - -func init() { - - var pt = PingTestlet{} - - // Ensure the RunFunction attribute is set correctly. - // This allows the underlying testlet infrastructure - // to know what function to call at runtime - pt.RunFunction = pt.RunTestlet - - // This is important - register the name of this testlet - // (the name the user will use in a testrun definition) - testlets.Register("ping", &pt) -} - -// RunTestlet implements the core logic of the testlet. Don't worry about running asynchronously, -// that's handled by the infrastructure. -func (p PingTestlet) RunTestlet(target string, args []string, kill chan (bool)) (map[string]string, error) { - - // Get number of pings - count := 3 //TODO(mierdin): need to parse from 'args', or if omitted, use a default value - - log.Error(args) - - var latencies []float32 - var replies int - - // Execute ping once per count - i := 0 - for i < count { - select { - case <-kill: - // Terminating early; return empty metrics - return map[string]string{}, nil - default: - - //log.Debugf("Executing ping #%d", i) - - // Mocked ping logic - latency, replyReceived := pingTemp(count) - - latencies = append(latencies, latency) - - if replyReceived { - replies += 1 - } - - i += 1 - time.Sleep(1000 * time.Millisecond) - - } - } - - // Calculate metrics - var latencyTotal float32 = 0 - for _, value := range latencies { - latencyTotal += value - } - avg_latency_ms := latencyTotal / float32(len(latencies)) - packet_loss := (float32(count) - float32(replies)) / float32(count) - - return map[string]string{ - "avg_latency_ms": fmt.Sprintf("%.2f", avg_latency_ms), - "packet_loss": fmt.Sprintf("%.2f", packet_loss), - }, nil - -} - -func pingTemp(count int) (float32, bool) { - return float32(count) * 4.234, true -} diff --git a/assets/assets_unpack.go b/assets/assets_unpack.go index 8f23ace..2a4a303 100644 --- a/assets/assets_unpack.go +++ b/assets/assets_unpack.go @@ -1,8 +1,8 @@ // Code generated by go-bindata. // sources: -// agent/testing/testlets/http -// agent/testing/testlets/iperf -// agent/testing/testlets/ping +// agent/testing/bashtestlets/http +// agent/testing/bashtestlets/iperf +// agent/testing/bashtestlets/notatallping // agent/facts/collectors/get_addresses // agent/facts/collectors/get_hostname // DO NOT EDIT! @@ -72,62 +72,62 @@ func (fi bindataFileInfo) Sys() interface{} { return nil } -var _testingTestletsHttp = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xa4\x97\x5f\x53\xdb\x38\x17\xc6\xef\xfd\x29\xce\x6b\xf2\x36\xa5\x03\x71\xc3\xc5\x5e\xd0\x81\x59\x1a\x42\xa1\x0d\x7f\x9a\xc0\xec\xee\x34\x9d\x8c\x63\x2b\x89\xa7\x8e\xe4\xb5\xe4\xb0\x2c\xf0\xdd\xf7\x48\xb2\x65\xc9\xa1\x21\xb3\xbb\x37\x6b\x1d\xff\xce\x63\xe9\xe8\x9c\x27\x74\xe7\x7f\x41\xc1\xf3\x60\x9a\xd0\x80\xd0\x15\x4c\x43\xbe\xf0\xbc\x1d\xe8\xb1\xec\x21\x4f\xe6\x0b\x01\x07\xef\xbb\xbf\xc0\x65\x28\x04\x5c\xf3\xfb\x30\x15\x1d\xb8\xe3\x04\x58\x0e\x4b\x16\x27\xb3\x24\x0a\x45\xc2\x28\xb0\x19\x88\x45\xc2\x31\x93\xb3\x22\x8f\x08\x44\x2c\x26\x90\x70\x98\xb3\x15\xc9\x29\x89\x61\xfa\x80\x04\x81\x34\x89\x08\x45\x81\x2c\x67\xab\x24\xc6\xf8\x82\xe4\xe4\x10\xf3\x16\x42\x64\xfc\x30\x08\xe6\x89\x58\x14\xd3\x4e\xc4\x96\xc1\x65\x42\xf2\x18\x37\x26\x58\x1c\x07\xd3\x94\x4d\x83\x65\xc8\x05\xc9\x83\xc1\x45\xaf\x7f\x35\xea\x7b\x5e\xb4\x20\xd1\x8f\xb7\xbb\xf0\xe8\x01\xfe\x47\xa2\x05\x03\x7f\x58\x50\x9a\xd0\x39\x24\x14\xd4\x6b\xb9\x51\xe2\x2b\x00\x45\x97\x21\x8d\x61\x7f\x05\x51\x91\xa7\x70\x1c\xc4\x64\x15\xd0\x22\x4d\xe1\xe0\xf8\x4d\x17\x9e\x9e\xe0\x51\xab\x1c\xbf\x39\x00\x5f\x31\x78\x06\xca\x04\xaa\x71\x11\xa6\x29\x89\x3b\xd0\xff\x2b\x11\xf8\x81\x8e\xff\x01\x08\x3e\x42\xf7\x03\x3c\x5b\xdf\xef\x99\x8f\xc2\xcd\xc9\x68\xd4\x3f\xd5\xdf\x56\xe8\x7b\xef\xd9\xf3\x92\x19\x7c\xfb\x06\xad\x2e\x1c\x1d\xe1\x37\x24\xed\xc3\xf7\xef\x1f\x90\xc2\x02\x51\xbd\x51\x19\xf5\x66\x89\xbc\x8a\x5b\xac\xda\x2c\xc9\xb9\x80\x30\x9f\x17\x4b\x42\xf1\x61\x86\x55\x50\xe5\x14\x84\x8b\x94\x08\xa0\xe1\x92\xc0\x7d\x82\x07\x39\x19\xfc\x76\xf2\xc7\x08\xa6\x44\xbf\xc7\x1c\x82\x77\x36\x62\x7b\xf2\x8b\x78\x98\x30\xbd\x0f\x1f\xb8\xf5\x72\x0f\x64\x49\xf0\x6c\x46\x9f\xc3\x2c\x67\x4b\x68\x1d\xa8\x37\x45\x06\x7c\xc1\x8a\x34\x96\x9a\x59\xc8\x39\x5e\x5a\x42\x05\x53\x12\x61\x96\x55\x55\xf5\xf0\x79\x82\x12\xfc\xc8\x6f\xfd\xea\xd7\xab\xd6\x63\xf5\xb8\xd3\x7a\x0f\xcf\xb0\x03\x43\xb2\xc4\xae\x80\x5b\x6b\xef\x2f\xe3\x5d\x07\x57\xbb\xc5\x82\x84\x42\xf5\x1a\x64\x0c\xb7\xb1\x07\x15\x6e\xed\x52\x1e\x46\x2c\x42\xd1\xe6\x90\x92\x99\xf0\x3c\x56\x88\xac\x10\x47\xad\xb7\xea\x4a\xf7\x39\xec\x33\xa8\xef\x7e\xff\x1e\xc6\xaa\xec\xed\xb1\x2e\xff\x98\x9e\xdf\xde\xde\xe0\x10\xc4\xe4\x10\xfe\xff\x28\x5b\x73\x22\x1b\xfa\x19\x26\xfd\xab\xd3\x13\x43\x0d\x18\xfb\x81\xe5\x11\xc9\x52\x71\xf2\xff\x13\x79\x9a\x54\xc5\x35\xfd\xd1\xd0\x3d\x46\x29\x89\x84\x8b\x47\x3a\xa8\xd9\x9e\x61\x6f\x72\xf2\xfb\x4c\xde\xb1\xcd\x66\x39\x11\x79\x48\x39\xbe\xd0\xfc\xa9\xe1\x47\x78\x97\x62\x3d\x83\xcb\xb0\x9b\xd3\x37\x39\xb7\x0c\x3b\xda\xe5\x85\x0c\x69\xee\xcc\xde\xb7\x90\x5d\x27\x1e\x32\x45\x46\x7a\x3d\x91\x6b\xcd\x7e\x6a\xd6\x4d\x9d\xc9\x2a\x9d\x75\xc6\x73\xc3\x5e\x15\xcb\x29\x6e\x18\xad\xa3\x4c\x40\x23\xe1\x32\x89\x16\xcb\x2a\x87\xeb\xa4\x8b\x17\x92\x86\x24\x4e\xf2\x46\x56\x5e\xc6\xca\xb4\xcf\x75\x7d\x92\xbf\x89\x4c\x8a\xd9\x3d\x4d\x59\x18\xcb\x04\x8e\xb1\x49\x15\xd0\x09\x5f\xd6\x12\x16\x24\x8c\x49\xce\x0d\xaf\xd7\x9a\x1e\xac\xd1\x39\xf9\xb3\xc0\xae\x36\x74\xb9\xd6\xf8\xe5\x1a\x5e\x64\xce\x5e\xf4\x52\xc3\x57\x06\x3e\x59\xcd\xe1\xb4\xdc\x25\x8c\x32\x42\x74\x82\x7c\x68\xec\xfe\xda\xc9\xb9\xcb\x5e\xca\xb0\xbf\x71\x63\xf8\xaa\x96\x6e\x37\x54\xd5\xd4\xf4\xd7\xba\x71\xf0\x25\xa0\x01\xa8\xee\x82\xaa\xbd\x36\x35\xdd\xb0\x1e\x19\x74\x70\xb8\x1b\x0e\xe0\x8c\x08\x74\x39\xb5\x33\x9c\xc9\x09\x99\xcd\xe4\x5d\xae\xca\x8e\x1a\x99\x84\x36\xce\x66\xab\xbb\xeb\x79\x66\x0a\x71\x8c\x95\xd3\xb6\xf4\x54\xc3\x13\x48\x4b\xda\xa7\xd0\xe6\x41\xe7\x9d\x35\xb9\xe3\xb7\x9d\x77\xe3\x5d\x3d\xb3\x9d\x77\xc1\xb8\x1b\x64\xed\x5d\xaf\x31\xa5\x9b\xd5\x9c\x09\xb7\xf4\x3e\x36\xf4\xca\x76\xdd\x2c\xe6\x1a\x80\xa5\xd6\x6b\xa8\x59\x83\xbe\x59\xd1\xb5\x09\x4b\xf1\xb4\xa1\xe8\xdc\xc9\x66\xcd\xa6\x95\x58\xaa\xfd\x86\xaa\xb2\x8b\xcd\x6a\xb6\xc9\x58\x4a\x67\xb5\x92\x6d\x27\xaf\xd6\xcf\x32\x22\x4b\xed\x53\xad\x66\x1b\xce\x56\x8d\x52\x5a\x95\xa5\x76\x5e\xab\xd9\x4e\xb4\x59\xed\x27\x66\x66\xc9\x5e\xb8\xb2\xc6\xaa\xb6\xd5\x75\xfd\xce\x12\xfe\x5c\x0b\x3b\x96\xf6\xca\x35\x37\x1d\xd1\xd1\xfc\xd2\xd0\xd4\xb6\xb7\x9d\xa2\xb1\x4c\x4b\x6f\xd0\xd0\x2b\x8d\x71\x3b\x41\xe3\xaa\x96\xe0\x65\x43\x50\xfb\xda\x76\x7a\x95\xed\x5a\x72\x57\x96\x9c\xe3\xac\x9b\x15\x5f\xf2\x66\x4b\xf5\xba\xa9\xba\xcd\x2e\xd7\xbd\xdb\x52\xbc\x69\x4c\x60\xd5\x45\x9b\x25\x1b\xf6\x6e\xe9\x7d\xfd\xf7\x3e\xf1\xb3\x5f\x01\x4b\x7d\x58\xab\x3b\x1e\xff\x8a\xe7\xae\xfd\x44\x58\x92\xa3\x5a\xd2\xc3\x3f\x7e\xf1\xef\xc7\x49\x1c\x8a\xf0\xa8\xad\xff\x09\xe0\x9b\x5f\x09\xff\xd0\x6f\xb7\xcc\xaa\xed\xef\xe9\xf7\x0d\xf7\x57\x54\x23\xe6\xb2\xe5\xf8\xd7\x60\x19\x70\x29\xcb\xb1\x6b\xd2\x0a\xba\xb4\x53\xe5\x9a\x77\xc2\x6e\x86\x72\xda\x9a\x54\x4b\x43\xd8\x0e\xaa\x18\x3b\x60\x28\xdb\x19\xed\xe2\xb8\xa7\xb1\x1d\x4f\x51\x76\xc0\xa1\x8c\x81\x19\xcc\x44\x0c\xe7\xf8\x91\xe2\x9c\x88\xcb\x69\xdf\xa8\x29\xbd\x76\x99\xd2\x0a\x6a\xa8\x0c\xb8\x94\x1e\xb3\x1a\xd2\xeb\x9a\x71\x26\x5c\x63\x4e\xa8\x41\xda\x72\x56\xc0\xbd\xa1\xea\xf0\xf5\x25\x55\x91\xff\x70\xf7\xce\xd4\x28\xd8\x89\xb4\x7d\xef\xb9\xed\xe9\x51\xb2\x86\xc1\xfb\x27\x00\x00\xff\xff\x75\x36\xff\x66\xd5\x0f\x00\x00") +var _testingBashtestletsHttp = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xa4\x97\x5f\x53\xdb\x38\x17\xc6\xef\xfd\x29\xce\x6b\xf2\x36\xa5\x03\x71\xc3\xc5\x5e\xd0\x81\x59\x1a\x42\xa1\x0d\x7f\x9a\xc0\xec\xee\x34\x9d\x8c\x63\x2b\x89\xa7\x8e\xe4\xb5\xe4\xb0\x2c\xf0\xdd\xf7\x48\xb2\x65\xc9\xa1\x21\xb3\xbb\x37\x6b\x1d\xff\xce\x63\xe9\xe8\x9c\x27\x74\xe7\x7f\x41\xc1\xf3\x60\x9a\xd0\x80\xd0\x15\x4c\x43\xbe\xf0\xbc\x1d\xe8\xb1\xec\x21\x4f\xe6\x0b\x01\x07\xef\xbb\xbf\xc0\x65\x28\x04\x5c\xf3\xfb\x30\x15\x1d\xb8\xe3\x04\x58\x0e\x4b\x16\x27\xb3\x24\x0a\x45\xc2\x28\xb0\x19\x88\x45\xc2\x31\x93\xb3\x22\x8f\x08\x44\x2c\x26\x90\x70\x98\xb3\x15\xc9\x29\x89\x61\xfa\x80\x04\x81\x34\x89\x08\x45\x81\x2c\x67\xab\x24\xc6\xf8\x82\xe4\xe4\x10\xf3\x16\x42\x64\xfc\x30\x08\xe6\x89\x58\x14\xd3\x4e\xc4\x96\xc1\x65\x42\xf2\x18\x37\x26\x58\x1c\x07\xd3\x94\x4d\x83\x65\xc8\x05\xc9\x83\xc1\x45\xaf\x7f\x35\xea\x7b\x5e\xb4\x20\xd1\x8f\xb7\xbb\xf0\xe8\x01\xfe\x47\xa2\x05\x03\x7f\x58\x50\x9a\xd0\x39\x24\x14\xd4\x6b\xb9\x51\xe2\x2b\x00\x45\x97\x21\x8d\x61\x7f\x05\x51\x91\xa7\x70\x1c\xc4\x64\x15\xd0\x22\x4d\xe1\xe0\xf8\x4d\x17\x9e\x9e\xe0\x51\xab\x1c\xbf\x39\x00\x5f\x31\x78\x06\xca\x04\xaa\x71\x11\xa6\x29\x89\x3b\xd0\xff\x2b\x11\xf8\x81\x8e\xff\x01\x08\x3e\x42\xf7\x03\x3c\x5b\xdf\xef\x99\x8f\xc2\xcd\xc9\x68\xd4\x3f\xd5\xdf\x56\xe8\x7b\xef\xd9\xf3\x92\x19\x7c\xfb\x06\xad\x2e\x1c\x1d\xe1\x37\x24\xed\xc3\xf7\xef\x1f\x90\xc2\x02\x51\xbd\x51\x19\xf5\x66\x89\xbc\x8a\x5b\xac\xda\x2c\xc9\xb9\x80\x30\x9f\x17\x4b\x42\xf1\x61\x86\x55\x50\xe5\x14\x84\x8b\x94\x08\xa0\xe1\x92\xc0\x7d\x82\x07\x39\x19\xfc\x76\xf2\xc7\x08\xa6\x44\xbf\xc7\x1c\x82\x77\x36\x62\x7b\xf2\x8b\x78\x98\x30\xbd\x0f\x1f\xb8\xf5\x72\x0f\x64\x49\xf0\x6c\x46\x9f\xc3\x2c\x67\x4b\x68\x1d\xa8\x37\x45\x06\x7c\xc1\x8a\x34\x96\x9a\x59\xc8\x39\x5e\x5a\x42\x05\x53\x12\x61\x96\x55\x55\xf5\xf0\x79\x82\x12\xfc\xc8\x6f\xfd\xea\xd7\xab\xd6\x63\xf5\xb8\xd3\x7a\x0f\xcf\xb0\x03\x43\xb2\xc4\xae\x80\x5b\x6b\xef\x2f\xe3\x5d\x07\x57\xbb\xc5\x82\x84\x42\xf5\x1a\x64\x0c\xb7\xb1\x07\x15\x6e\xed\x52\x1e\x46\x2c\x42\xd1\xe6\x90\x92\x99\xf0\x3c\x56\x88\xac\x10\x47\xad\xb7\xea\x4a\xf7\x39\xec\x33\xa8\xef\x7e\xff\x1e\xc6\xaa\xec\xed\xb1\x2e\xff\x98\x9e\xdf\xde\xde\xe0\x10\xc4\xe4\x10\xfe\xff\x28\x5b\x73\x22\x1b\xfa\x19\x26\xfd\xab\xd3\x13\x43\x0d\x18\xfb\x81\xe5\x11\xc9\x52\x71\xf2\xff\x13\x79\x9a\x54\xc5\x35\xfd\xd1\xd0\x3d\x46\x29\x89\x84\x8b\x47\x3a\xa8\xd9\x9e\x61\x6f\x72\xf2\xfb\x4c\xde\xb1\xcd\x66\x39\x11\x79\x48\x39\xbe\xd0\xfc\xa9\xe1\x47\x78\x97\x62\x3d\x83\xcb\xb0\x9b\xd3\x37\x39\xb7\x0c\x3b\xda\xe5\x85\x0c\x69\xee\xcc\xde\xb7\x90\x5d\x27\x1e\x32\x45\x46\x7a\x3d\x91\x6b\xcd\x7e\x6a\xd6\x4d\x9d\xc9\x2a\x9d\x75\xc6\x73\xc3\x5e\x15\xcb\x29\x6e\x18\xad\xa3\x4c\x40\x23\xe1\x32\x89\x16\xcb\x2a\x87\xeb\xa4\x8b\x17\x92\x86\x24\x4e\xf2\x46\x56\x5e\xc6\xca\xb4\xcf\x75\x7d\x92\xbf\x89\x4c\x8a\xd9\x3d\x4d\x59\x18\xcb\x04\x8e\xb1\x49\x15\xd0\x09\x5f\xd6\x12\x16\x24\x8c\x49\xce\x0d\xaf\xd7\x9a\x1e\xac\xd1\x39\xf9\xb3\xc0\xae\x36\x74\xb9\xd6\xf8\xe5\x1a\x5e\x64\xce\x5e\xf4\x52\xc3\x57\x06\x3e\x59\xcd\xe1\xb4\xdc\x25\x8c\x32\x42\x74\x82\x7c\x68\xec\xfe\xda\xc9\xb9\xcb\x5e\xca\xb0\xbf\x71\x63\xf8\xaa\x96\x6e\x37\x54\xd5\xd4\xf4\xd7\xba\x71\xf0\x25\xa0\x01\xa8\xee\x82\xaa\xbd\x36\x35\xdd\xb0\x1e\x19\x74\x70\xb8\x1b\x0e\xe0\x8c\x08\x74\x39\xb5\x33\x9c\xc9\x09\x99\xcd\xe4\x5d\xae\xca\x8e\x1a\x99\x84\x36\xce\x66\xab\xbb\xeb\x79\x66\x0a\x71\x8c\x95\xd3\xb6\xf4\x54\xc3\x13\x48\x4b\xda\xa7\xd0\xe6\x41\xe7\x9d\x35\xb9\xe3\xb7\x9d\x77\xe3\x5d\x3d\xb3\x9d\x77\xc1\xb8\x1b\x64\xed\x5d\xaf\x31\xa5\x9b\xd5\x9c\x09\xb7\xf4\x3e\x36\xf4\xca\x76\xdd\x2c\xe6\x1a\x80\xa5\xd6\x6b\xa8\x59\x83\xbe\x59\xd1\xb5\x09\x4b\xf1\xb4\xa1\xe8\xdc\xc9\x66\xcd\xa6\x95\x58\xaa\xfd\x86\xaa\xb2\x8b\xcd\x6a\xb6\xc9\x58\x4a\x67\xb5\x92\x6d\x27\xaf\xd6\xcf\x32\x22\x4b\xed\x53\xad\x66\x1b\xce\x56\x8d\x52\x5a\x95\xa5\x76\x5e\xab\xd9\x4e\xb4\x59\xed\x27\x66\x66\xc9\x5e\xb8\xb2\xc6\xaa\xb6\xd5\x75\xfd\xce\x12\xfe\x5c\x0b\x3b\x96\xf6\xca\x35\x37\x1d\xd1\xd1\xfc\xd2\xd0\xd4\xb6\xb7\x9d\xa2\xb1\x4c\x4b\x6f\xd0\xd0\x2b\x8d\x71\x3b\x41\xe3\xaa\x96\xe0\x65\x43\x50\xfb\xda\x76\x7a\x95\xed\x5a\x72\x57\x96\x9c\xe3\xac\x9b\x15\x5f\xf2\x66\x4b\xf5\xba\xa9\xba\xcd\x2e\xd7\xbd\xdb\x52\xbc\x69\x4c\x60\xd5\x45\x9b\x25\x1b\xf6\x6e\xe9\x7d\xfd\xf7\x3e\xf1\xb3\x5f\x01\x4b\x7d\x58\xab\x3b\x1e\xff\x8a\xe7\xae\xfd\x44\x58\x92\xa3\x5a\xd2\xc3\x3f\x7e\xf1\xef\xc7\x49\x1c\x8a\xf0\xa8\xad\xff\x09\xe0\x9b\x5f\x09\xff\xd0\x6f\xb7\xcc\xaa\xed\xef\xe9\xf7\x0d\xf7\x57\x54\x23\xe6\xb2\xe5\xf8\xd7\x60\x19\x70\x29\xcb\xb1\x6b\xd2\x0a\xba\xb4\x53\xe5\x9a\x77\xc2\x6e\x86\x72\xda\x9a\x54\x4b\x43\xd8\x0e\xaa\x18\x3b\x60\x28\xdb\x19\xed\xe2\xb8\xa7\xb1\x1d\x4f\x51\x76\xc0\xa1\x8c\x81\x19\xcc\x44\x0c\xe7\xf8\x91\xe2\x9c\x88\xcb\x69\xdf\xa8\x29\xbd\x76\x99\xd2\x0a\x6a\xa8\x0c\xb8\x94\x1e\xb3\x1a\xd2\xeb\x9a\x71\x26\x5c\x63\x4e\xa8\x41\xda\x72\x56\xc0\xbd\xa1\xea\xf0\xf5\x25\x55\x91\xff\x70\xf7\xce\xd4\x28\xd8\x89\xb4\x7d\xef\xb9\xed\xe9\x51\xb2\x86\xc1\xfb\x27\x00\x00\xff\xff\x75\x36\xff\x66\xd5\x0f\x00\x00") -func testingTestletsHttpBytes() ([]byte, error) { +func testingBashtestletsHttpBytes() ([]byte, error) { return bindataRead( - _testingTestletsHttp, - "testing/testlets/http", + _testingBashtestletsHttp, + "testing/bashtestlets/http", ) } -func testingTestletsHttp() (*asset, error) { - bytes, err := testingTestletsHttpBytes() +func testingBashtestletsHttp() (*asset, error) { + bytes, err := testingBashtestletsHttpBytes() if err != nil { return nil, err } - info := bindataFileInfo{name: "testing/testlets/http", size: 4053, mode: os.FileMode(493), modTime: time.Unix(1462644938, 0)} + info := bindataFileInfo{name: "testing/bashtestlets/http", size: 4053, mode: os.FileMode(493), modTime: time.Unix(1463727404, 0)} a := &asset{bytes: bytes, info: info} return a, nil } -var _testingTestletsIperf = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x94\x54\x4d\x6f\xe3\x36\x10\xbd\xeb\x57\x4c\x19\x63\x95\x04\x8e\x15\xe7\xd0\x43\x02\x07\x4d\xd3\x1c\x02\x6c\xb6\xc1\x3a\xc5\xa2\xd8\x2c\x0c\x4a\x1a\x59\x6c\x24\x52\x20\x47\x56\x5d\xc7\xff\xbd\x43\xd1\x5f\xbb\xf0\x65\xed\x0b\xcd\x99\x79\x7c\xef\xcd\x8c\x4f\x7e\x49\x5a\x67\x93\x54\xe9\x04\xf5\x02\x52\xe9\xca\x28\x3a\x81\x7b\xd3\x2c\xad\x9a\x97\x04\x57\x97\xe3\x5f\xe1\x49\x12\xc1\x9f\xae\x93\x15\x8d\xe0\x2f\x87\x60\x2c\xd4\x26\x57\x85\xca\x24\x29\xa3\xc1\x14\x40\xa5\x72\x5c\xe9\x4c\x6b\x33\x84\xcc\xe4\x08\xca\xc1\xdc\x2c\xd0\x6a\xcc\x21\x5d\x72\x06\x42\xa5\x32\xd4\x0c\xd0\x58\xb3\x50\x39\xdf\x97\x68\xf1\x9a\xeb\x4a\xa2\xc6\x5d\x27\xc9\x5c\x51\xd9\xa6\xa3\xcc\xd4\xc9\x93\x42\x9b\x33\x31\x32\x79\x9e\xa4\x95\x49\x93\x5a\x3a\x42\x9b\x7c\x7c\xbc\x7f\xf8\x34\x7d\x88\xa2\xac\xc4\xec\xed\xf4\x0c\x56\x11\xf0\x07\xb3\xd2\x80\xf8\xdc\x6a\xad\xf4\x1c\x94\x86\x3e\xec\x89\xa2\xe8\x13\x18\xb4\x96\x3a\x87\x8b\x05\xa8\x06\x6d\x01\xb7\x49\x8e\x8b\x44\xb7\x55\x05\x57\xb7\x1f\xc6\xf0\xfe\x0e\xab\x00\x73\xfb\xe1\x0a\x44\x48\x62\x15\xda\x10\xe3\x39\x92\x55\x85\xf9\x08\x1e\xfe\x55\xc4\x4f\x8c\xc4\x0d\x20\x1f\x61\x7c\x03\xeb\x03\x06\xf7\xbb\x67\xe1\xf9\x6e\x3a\x7d\xf8\x23\xbc\xde\xa7\x5e\x46\xeb\x28\x52\x05\x7c\xfd\x0a\x83\x31\x4c\x26\x20\x7a\x92\x02\xbe\x7d\xbb\xe1\x2c\xb6\x48\x07\xaa\xfe\x36\x2a\x94\x6f\xc6\x0b\xfb\x56\x28\xeb\x08\xa4\x9d\xb7\x35\x6a\x3e\x14\xec\x43\x6f\x28\xa1\xa3\x0a\x09\xb4\xac\x11\x3a\xc5\x4a\xee\x3e\x7e\xb9\xfb\x7b\x0a\x29\x86\x38\xd7\x20\x77\x6d\x6a\x86\xfe\x45\x16\x23\xab\x4e\x2e\xdd\x41\x70\x08\xde\x14\xd6\xb6\xc3\x77\x50\x58\x53\xc3\xe0\xaa\x8f\xb4\x0d\xb8\xd2\xb4\x55\xee\x31\x1b\xe9\x1c\xb7\x4d\x69\x32\x3d\x84\x6c\x9a\xad\xaf\x11\x9f\x67\x0c\xe1\x26\x62\xf0\x9b\xd8\xff\x1a\xac\xb6\xc7\x93\xc1\x25\xac\xe1\x04\x3e\x63\xcd\x73\x01\x2f\x07\xdc\x8f\xa7\x8f\xbf\x4b\xef\xd9\xb2\x21\x92\xfa\x69\x83\xc6\x30\x8d\x21\x6c\xd3\x0f\x58\x7a\x31\x54\x4a\x8a\x1d\x54\x58\x50\x70\x91\x2b\xb6\x6e\xb5\x0e\xdd\x46\x3d\xb8\x36\x75\xa4\xa8\xf5\x83\x3c\xe4\xf9\xe5\x0a\x5f\x57\xcb\x37\xe4\x98\xc5\x1e\x08\xb8\x67\xdc\x3e\xee\xa1\x63\x77\x78\xb6\x7a\xed\xfc\xe8\x10\x3a\x04\x8b\x4d\x25\x79\xea\x39\xa3\xe3\xf9\x0d\xc1\x8c\x5a\x59\x6d\x1f\x79\x7c\x1e\x1d\x28\x3c\xed\x07\x65\xb0\x23\xfe\x0e\xde\xd3\xd8\x25\xab\xd5\xb6\x60\xbd\x4e\x62\x31\x18\x8b\x38\x89\xcf\x3c\xfd\x4f\x86\x36\x4c\x3a\x8c\x99\x54\x61\x6c\xe6\xc7\xbc\xc6\xb9\x4c\x15\xeb\xea\x37\x89\x85\xf9\x4b\x71\x51\x40\x2d\xbc\x16\x26\x97\x49\x0d\xa5\x5c\xf8\x9d\xc3\x5c\x65\x24\xd3\xca\xb7\xd1\xfa\xcc\x11\x7c\xe1\x73\xeb\xfd\xc4\x3a\xf8\x8a\x80\xdc\x73\x67\xc2\x63\x52\x2f\xb9\xbd\xba\xe0\xbd\xf5\x23\xef\x8d\xb3\xfb\xe5\xed\xc9\x77\x46\xc7\xb4\x4b\xda\x1a\xa0\x5c\x10\x6c\x5b\x3d\xdb\xcc\xc7\x64\xb3\x50\x7b\xdd\x9e\x26\x88\x28\x32\x2d\x31\x09\xf6\x65\xf0\x43\x09\x4b\xa7\xac\x99\x75\x4a\xe7\xa6\x9b\x39\xf5\x1f\xce\xea\x74\x49\xb8\xb3\x30\x54\xb2\x81\x64\x21\x7e\xd5\x31\xf8\x6f\xb0\xf3\x42\x7b\x47\x47\xe7\x2f\xf7\xcf\x10\x00\xc0\x03\x5c\xc3\xeb\xe9\xe8\xfc\xf5\x6c\x74\x9e\xbc\x8e\x93\x26\xde\x9b\x0f\x4f\xbf\x33\x34\xdf\xb3\xe5\x00\x7e\x62\xd8\x80\x8a\xff\x72\xfa\x04\xde\x7e\xc2\x7e\xf9\xc2\x98\x79\xd1\x3c\x69\x1a\x33\x74\x4e\xda\x25\xa4\xcc\xe3\x91\x7d\xd8\xc4\x1c\xb2\xa3\xbc\x24\xbe\x99\xb4\xdb\x60\xa3\xd1\x5f\x76\xc6\xbe\xc1\x3f\x2d\x5f\x78\x47\x30\xa3\x6a\x19\x91\x95\xda\x15\x68\x83\x42\xf7\x33\x12\x1d\x66\x70\x1e\x64\x05\x11\x6e\xab\xee\x2c\x4a\xd9\xc6\x4e\xe5\x54\x32\xae\x22\x37\xe3\xdc\x9f\x81\x0e\x70\x7b\x74\x8f\xb1\x07\x8f\x38\xc6\xeb\x32\xcb\x25\xc9\x49\xbc\x12\x47\x9b\x25\xae\x45\x3c\x38\x1a\x89\xc5\x10\xc4\x0f\xba\x43\xf6\xf7\x77\x7d\xde\x11\x21\x7d\xee\x91\xfb\x58\xac\xe3\x28\x48\x3c\x20\x18\xfd\x1f\x00\x00\xff\xff\x4d\x99\x39\x1a\xed\x06\x00\x00") +var _testingBashtestletsIperf = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x94\x54\x4d\x6f\xe3\x36\x10\xbd\xeb\x57\x4c\x19\x63\x95\x04\x8e\x15\xe7\xd0\x43\x02\x07\x4d\xd3\x1c\x02\x6c\xb6\xc1\x3a\xc5\xa2\xd8\x2c\x0c\x4a\x1a\x59\x6c\x24\x52\x20\x47\x56\x5d\xc7\xff\xbd\x43\xd1\x5f\xbb\xf0\x65\xed\x0b\xcd\x99\x79\x7c\xef\xcd\x8c\x4f\x7e\x49\x5a\x67\x93\x54\xe9\x04\xf5\x02\x52\xe9\xca\x28\x3a\x81\x7b\xd3\x2c\xad\x9a\x97\x04\x57\x97\xe3\x5f\xe1\x49\x12\xc1\x9f\xae\x93\x15\x8d\xe0\x2f\x87\x60\x2c\xd4\x26\x57\x85\xca\x24\x29\xa3\xc1\x14\x40\xa5\x72\x5c\xe9\x4c\x6b\x33\x84\xcc\xe4\x08\xca\xc1\xdc\x2c\xd0\x6a\xcc\x21\x5d\x72\x06\x42\xa5\x32\xd4\x0c\xd0\x58\xb3\x50\x39\xdf\x97\x68\xf1\x9a\xeb\x4a\xa2\xc6\x5d\x27\xc9\x5c\x51\xd9\xa6\xa3\xcc\xd4\xc9\x93\x42\x9b\x33\x31\x32\x79\x9e\xa4\x95\x49\x93\x5a\x3a\x42\x9b\x7c\x7c\xbc\x7f\xf8\x34\x7d\x88\xa2\xac\xc4\xec\xed\xf4\x0c\x56\x11\xf0\x07\xb3\xd2\x80\xf8\xdc\x6a\xad\xf4\x1c\x94\x86\x3e\xec\x89\xa2\xe8\x13\x18\xb4\x96\x3a\x87\x8b\x05\xa8\x06\x6d\x01\xb7\x49\x8e\x8b\x44\xb7\x55\x05\x57\xb7\x1f\xc6\xf0\xfe\x0e\xab\x00\x73\xfb\xe1\x0a\x44\x48\x62\x15\xda\x10\xe3\x39\x92\x55\x85\xf9\x08\x1e\xfe\x55\xc4\x4f\x8c\xc4\x0d\x20\x1f\x61\x7c\x03\xeb\x03\x06\xf7\xbb\x67\xe1\xf9\x6e\x3a\x7d\xf8\x23\xbc\xde\xa7\x5e\x46\xeb\x28\x52\x05\x7c\xfd\x0a\x83\x31\x4c\x26\x20\x7a\x92\x02\xbe\x7d\xbb\xe1\x2c\xb6\x48\x07\xaa\xfe\x36\x2a\x94\x6f\xc6\x0b\xfb\x56\x28\xeb\x08\xa4\x9d\xb7\x35\x6a\x3e\x14\xec\x43\x6f\x28\xa1\xa3\x0a\x09\xb4\xac\x11\x3a\xc5\x4a\xee\x3e\x7e\xb9\xfb\x7b\x0a\x29\x86\x38\xd7\x20\x77\x6d\x6a\x86\xfe\x45\x16\x23\xab\x4e\x2e\xdd\x41\x70\x08\xde\x14\xd6\xb6\xc3\x77\x50\x58\x53\xc3\xe0\xaa\x8f\xb4\x0d\xb8\xd2\xb4\x55\xee\x31\x1b\xe9\x1c\xb7\x4d\x69\x32\x3d\x84\x6c\x9a\xad\xaf\x11\x9f\x67\x0c\xe1\x26\x62\xf0\x9b\xd8\xff\x1a\xac\xb6\xc7\x93\xc1\x25\xac\xe1\x04\x3e\x63\xcd\x73\x01\x2f\x07\xdc\x8f\xa7\x8f\xbf\x4b\xef\xd9\xb2\x21\x92\xfa\x69\x83\xc6\x30\x8d\x21\x6c\xd3\x0f\x58\x7a\x31\x54\x4a\x8a\x1d\x54\x58\x50\x70\x91\x2b\xb6\x6e\xb5\x0e\xdd\x46\x3d\xb8\x36\x75\xa4\xa8\xf5\x83\x3c\xe4\xf9\xe5\x0a\x5f\x57\xcb\x37\xe4\x98\xc5\x1e\x08\xb8\x67\xdc\x3e\xee\xa1\x63\x77\x78\xb6\x7a\xed\xfc\xe8\x10\x3a\x04\x8b\x4d\x25\x79\xea\x39\xa3\xe3\xf9\x0d\xc1\x8c\x5a\x59\x6d\x1f\x79\x7c\x1e\x1d\x28\x3c\xed\x07\x65\xb0\x23\xfe\x0e\xde\xd3\xd8\x25\xab\xd5\xb6\x60\xbd\x4e\x62\x31\x18\x8b\x38\x89\xcf\x3c\xfd\x4f\x86\x36\x4c\x3a\x8c\x99\x54\x61\x6c\xe6\xc7\xbc\xc6\xb9\x4c\x15\xeb\xea\x37\x89\x85\xf9\x4b\x71\x51\x40\x2d\xbc\x16\x26\x97\x49\x0d\xa5\x5c\xf8\x9d\xc3\x5c\x65\x24\xd3\xca\xb7\xd1\xfa\xcc\x11\x7c\xe1\x73\xeb\xfd\xc4\x3a\xf8\x8a\x80\xdc\x73\x67\xc2\x63\x52\x2f\xb9\xbd\xba\xe0\xbd\xf5\x23\xef\x8d\xb3\xfb\xe5\xed\xc9\x77\x46\xc7\xb4\x4b\xda\x1a\xa0\x5c\x10\x6c\x5b\x3d\xdb\xcc\xc7\x64\xb3\x50\x7b\xdd\x9e\x26\x88\x28\x32\x2d\x31\x09\xf6\x65\xf0\x43\x09\x4b\xa7\xac\x99\x75\x4a\xe7\xa6\x9b\x39\xf5\x1f\xce\xea\x74\x49\xb8\xb3\x30\x54\xb2\x81\x64\x21\x7e\xd5\x31\xf8\x6f\xb0\xf3\x42\x7b\x47\x47\xe7\x2f\xf7\xcf\x10\x00\xc0\x03\x5c\xc3\xeb\xe9\xe8\xfc\xf5\x6c\x74\x9e\xbc\x8e\x93\x26\xde\x9b\x0f\x4f\xbf\x33\x34\xdf\xb3\xe5\x00\x7e\x62\xd8\x80\x8a\xff\x72\xfa\x04\xde\x7e\xc2\x7e\xf9\xc2\x98\x79\xd1\x3c\x69\x1a\x33\x74\x4e\xda\x25\xa4\xcc\xe3\x91\x7d\xd8\xc4\x1c\xb2\xa3\xbc\x24\xbe\x99\xb4\xdb\x60\xa3\xd1\x5f\x76\xc6\xbe\xc1\x3f\x2d\x5f\x78\x47\x30\xa3\x6a\x19\x91\x95\xda\x15\x68\x83\x42\xf7\x33\x12\x1d\x66\x70\x1e\x64\x05\x11\x6e\xab\xee\x2c\x4a\xd9\xc6\x4e\xe5\x54\x32\xae\x22\x37\xe3\xdc\x9f\x81\x0e\x70\x7b\x74\x8f\xb1\x07\x8f\x38\xc6\xeb\x32\xcb\x25\xc9\x49\xbc\x12\x47\x9b\x25\xae\x45\x3c\x38\x1a\x89\xc5\x10\xc4\x0f\xba\x43\xf6\xf7\x77\x7d\xde\x11\x21\x7d\xee\x91\xfb\x58\xac\xe3\x28\x48\x3c\x20\x18\xfd\x1f\x00\x00\xff\xff\x4d\x99\x39\x1a\xed\x06\x00\x00") -func testingTestletsIperfBytes() ([]byte, error) { +func testingBashtestletsIperfBytes() ([]byte, error) { return bindataRead( - _testingTestletsIperf, - "testing/testlets/iperf", + _testingBashtestletsIperf, + "testing/bashtestlets/iperf", ) } -func testingTestletsIperf() (*asset, error) { - bytes, err := testingTestletsIperfBytes() +func testingBashtestletsIperf() (*asset, error) { + bytes, err := testingBashtestletsIperfBytes() if err != nil { return nil, err } - info := bindataFileInfo{name: "testing/testlets/iperf", size: 1773, mode: os.FileMode(493), modTime: time.Unix(1462644938, 0)} + info := bindataFileInfo{name: "testing/bashtestlets/iperf", size: 1773, mode: os.FileMode(493), modTime: time.Unix(1463727404, 0)} a := &asset{bytes: bytes, info: info} return a, nil } -var _testingTestletsPing = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x94\x93\x4f\x4f\xdb\x4a\x14\xc5\xf7\xf3\x29\xce\x1b\xfc\x70\x82\x20\x43\xb2\x78\x0b\x50\xd0\x43\xbc\x2c\x9e\x04\x2d\x6a\x5a\x55\x15\x41\xd1\xc4\xbe\x89\x47\xd8\x33\x96\x67\x1c\x1a\x25\x7c\xf7\x5e\xdb\x05\xac\xaa\x9b\xc2\xe6\x66\xce\xb9\xbf\xfb\x2f\x39\xfa\x4b\xd5\xbe\x52\x2b\x63\x15\xd9\x2d\x56\xda\x67\x42\x1c\xe1\xc6\x95\xbb\xca\x6c\xb2\x80\xc9\xf9\xf8\x1f\xdc\xe9\x10\xf0\xd1\x3f\xeb\x3c\x8c\xf0\xc5\x13\x5c\x85\xc2\xa5\x66\x6d\x12\x1d\x8c\xb3\x70\x6b\x84\xcc\x78\xce\xf4\xae\xae\x12\x42\xe2\x52\x82\xf1\xd8\xb8\x2d\x55\x96\x52\xac\x76\xec\x20\xe4\x26\x21\xcb\x80\xb2\x72\x5b\x93\xf2\x7b\x46\x15\x5d\x70\x5e\x16\x42\xe9\x2f\x94\xda\x98\x90\xd5\xab\x51\xe2\x0a\x75\x67\xa8\x4a\xb9\xb1\xe0\xd2\x54\xad\x72\xb7\x52\x85\xf6\x81\x2a\x75\xfb\xff\xcd\xec\xc3\x7c\x26\x44\x92\x51\xf2\x34\x18\x62\x2f\xc0\x7f\x94\x64\x0e\xf2\x53\x6d\xad\xb1\x1b\x18\x8b\x56\x6e\x1a\x25\xd9\x1a\x18\x5a\x68\x9b\xe2\x6c\x8b\xb2\xb1\x5c\xa9\x94\xb6\xca\xd6\x79\x8e\xc9\xd5\xf1\x18\x87\x03\xf6\x1d\xe5\xea\x78\x02\x79\xdf\x62\x3c\xac\x0b\x4c\xf3\x41\xe7\x39\xa5\x23\xcc\xbe\x9b\xc0\xca\x48\x5e\x82\x38\xc4\xf8\x12\x2f\xbd\xfa\x37\x6f\x45\x71\x7f\x3d\x9f\xcf\xfe\xeb\x6a\xb7\xd6\x73\xf1\x22\x84\x59\xe3\xe1\x01\xd1\x18\xd3\x29\x64\xdb\xa2\xc4\xe3\xe3\x25\xbb\x78\x41\xb6\x6b\xb4\x79\x15\x6b\xd3\x9c\xe2\x33\x6f\x6d\x6d\x2a\x1f\xa0\xab\x4d\x5d\x90\xe5\x60\xcd\x5b\x68\xd7\x19\xc8\x87\x9c\x02\xac\x2e\x08\xcf\x86\x07\xb9\xbe\xfd\x7a\xfd\x6d\x8e\x15\x75\x3a\xe7\x10\xdf\x6c\xee\x4e\x9b\x8a\x3c\x8c\xce\x9f\xf5\xce\xf7\xc4\x53\x34\x2b\xe1\xd9\xde\xf8\x1e\xeb\xca\x15\x88\x26\xad\x52\x97\xf0\x99\xab\xf3\xb4\x61\x96\xda\x7b\x3e\x9a\xb1\xc1\xb5\x08\x5d\x96\xaf\x5b\x15\x1c\x2f\x19\xe1\xa7\x32\xfa\x57\xbe\x7f\x8a\xf6\xaf\xe1\x51\x74\xce\x9b\xfa\xad\x30\x66\xa1\x55\xaa\xda\x2e\x7f\x02\xa7\xb2\x3d\x52\xf4\xea\xe2\x01\xa4\x10\xae\x0e\x65\x1d\xa6\xd1\x20\xfa\xc5\x3e\x14\xa2\xd4\xc9\x13\x85\x65\xee\x3c\xd3\x07\xed\x39\xa2\xce\x8f\x03\x42\x85\x78\x61\x63\x34\xff\x07\x34\x53\x9c\x59\xc4\x5e\x8d\x4e\xb0\x18\x8c\x4e\x16\xc3\xbf\xd1\xe5\xa3\xc9\x1f\x9d\xa8\xc5\x58\x95\xf1\x50\xe8\xed\x66\x99\xeb\x40\x36\xd9\xfd\x09\x94\xbf\x59\x98\x82\xb9\xaa\xa3\x2f\x54\x13\xbf\x63\x05\x9f\x8e\x2f\xb7\x4c\x75\xd0\xd3\x78\x2f\x7b\xbd\x2f\x4b\xe2\x9f\x90\x0d\x7a\x43\xf2\x42\xc6\x51\x4f\x8a\xe5\x29\x64\xaf\xa3\x65\xe1\x5b\x4b\xef\x29\x96\x2f\xb1\xe8\xda\xec\x95\xf8\x11\x00\x00\xff\xff\x84\x27\x4a\xf0\xe8\x03\x00\x00") +var _testingBashtestletsNotatallping = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x94\x93\x4f\x4f\xdb\x4a\x14\xc5\xf7\xf3\x29\xce\x1b\xfc\x70\x82\x20\x43\xb2\x78\x0b\x50\xd0\x43\xbc\x2c\x9e\x04\x2d\x6a\x5a\x55\x15\x41\xd1\xc4\xbe\x89\x47\xd8\x33\x96\x67\x1c\x1a\x25\x7c\xf7\x5e\xdb\x05\xac\xaa\x9b\xc2\xe6\x66\xce\xb9\xbf\xfb\x2f\x39\xfa\x4b\xd5\xbe\x52\x2b\x63\x15\xd9\x2d\x56\xda\x67\x42\x1c\xe1\xc6\x95\xbb\xca\x6c\xb2\x80\xc9\xf9\xf8\x1f\xdc\xe9\x10\xf0\xd1\x3f\xeb\x3c\x8c\xf0\xc5\x13\x5c\x85\xc2\xa5\x66\x6d\x12\x1d\x8c\xb3\x70\x6b\x84\xcc\x78\xce\xf4\xae\xae\x12\x42\xe2\x52\x82\xf1\xd8\xb8\x2d\x55\x96\x52\xac\x76\xec\x20\xe4\x26\x21\xcb\x80\xb2\x72\x5b\x93\xf2\x7b\x46\x15\x5d\x70\x5e\x16\x42\xe9\x2f\x94\xda\x98\x90\xd5\xab\x51\xe2\x0a\x75\x67\xa8\x4a\xb9\xb1\xe0\xd2\x54\xad\x72\xb7\x52\x85\xf6\x81\x2a\x75\xfb\xff\xcd\xec\xc3\x7c\x26\x44\x92\x51\xf2\x34\x18\x62\x2f\xc0\x7f\x94\x64\x0e\xf2\x53\x6d\xad\xb1\x1b\x18\x8b\x56\x6e\x1a\x25\xd9\x1a\x18\x5a\x68\x9b\xe2\x6c\x8b\xb2\xb1\x5c\xa9\x94\xb6\xca\xd6\x79\x8e\xc9\xd5\xf1\x18\x87\x03\xf6\x1d\xe5\xea\x78\x02\x79\xdf\x62\x3c\xac\x0b\x4c\xf3\x41\xe7\x39\xa5\x23\xcc\xbe\x9b\xc0\xca\x48\x5e\x82\x38\xc4\xf8\x12\x2f\xbd\xfa\x37\x6f\x45\x71\x7f\x3d\x9f\xcf\xfe\xeb\x6a\xb7\xd6\x73\xf1\x22\x84\x59\xe3\xe1\x01\xd1\x18\xd3\x29\x64\xdb\xa2\xc4\xe3\xe3\x25\xbb\x78\x41\xb6\x6b\xb4\x79\x15\x6b\xd3\x9c\xe2\x33\x6f\x6d\x6d\x2a\x1f\xa0\xab\x4d\x5d\x90\xe5\x60\xcd\x5b\x68\xd7\x19\xc8\x87\x9c\x02\xac\x2e\x08\xcf\x86\x07\xb9\xbe\xfd\x7a\xfd\x6d\x8e\x15\x75\x3a\xe7\x10\xdf\x6c\xee\x4e\x9b\x8a\x3c\x8c\xce\x9f\xf5\xce\xf7\xc4\x53\x34\x2b\xe1\xd9\xde\xf8\x1e\xeb\xca\x15\x88\x26\xad\x52\x97\xf0\x99\xab\xf3\xb4\x61\x96\xda\x7b\x3e\x9a\xb1\xc1\xb5\x08\x5d\x96\xaf\x5b\x15\x1c\x2f\x19\xe1\xa7\x32\xfa\x57\xbe\x7f\x8a\xf6\xaf\xe1\x51\x74\xce\x9b\xfa\xad\x30\x66\xa1\x55\xaa\xda\x2e\x7f\x02\xa7\xb2\x3d\x52\xf4\xea\xe2\x01\xa4\x10\xae\x0e\x65\x1d\xa6\xd1\x20\xfa\xc5\x3e\x14\xa2\xd4\xc9\x13\x85\x65\xee\x3c\xd3\x07\xed\x39\xa2\xce\x8f\x03\x42\x85\x78\x61\x63\x34\xff\x07\x34\x53\x9c\x59\xc4\x5e\x8d\x4e\xb0\x18\x8c\x4e\x16\xc3\xbf\xd1\xe5\xa3\xc9\x1f\x9d\xa8\xc5\x58\x95\xf1\x50\xe8\xed\x66\x99\xeb\x40\x36\xd9\xfd\x09\x94\xbf\x59\x98\x82\xb9\xaa\xa3\x2f\x54\x13\xbf\x63\x05\x9f\x8e\x2f\xb7\x4c\x75\xd0\xd3\x78\x2f\x7b\xbd\x2f\x4b\xe2\x9f\x90\x0d\x7a\x43\xf2\x42\xc6\x51\x4f\x8a\xe5\x29\x64\xaf\xa3\x65\xe1\x5b\x4b\xef\x29\x96\x2f\xb1\xe8\xda\xec\x95\xf8\x11\x00\x00\xff\xff\x84\x27\x4a\xf0\xe8\x03\x00\x00") -func testingTestletsPingBytes() ([]byte, error) { +func testingBashtestletsNotatallpingBytes() ([]byte, error) { return bindataRead( - _testingTestletsPing, - "testing/testlets/ping", + _testingBashtestletsNotatallping, + "testing/bashtestlets/notatallping", ) } -func testingTestletsPing() (*asset, error) { - bytes, err := testingTestletsPingBytes() +func testingBashtestletsNotatallping() (*asset, error) { + bytes, err := testingBashtestletsNotatallpingBytes() if err != nil { return nil, err } - info := bindataFileInfo{name: "testing/testlets/ping", size: 1000, mode: os.FileMode(493), modTime: time.Unix(1462644962, 0)} + info := bindataFileInfo{name: "testing/bashtestlets/notatallping", size: 1000, mode: os.FileMode(493), modTime: time.Unix(1463727404, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -147,7 +147,7 @@ func factsCollectorsGet_addresses() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "facts/collectors/get_addresses", size: 421, mode: os.FileMode(493), modTime: time.Unix(1462644938, 0)} + info := bindataFileInfo{name: "facts/collectors/get_addresses", size: 421, mode: os.FileMode(493), modTime: time.Unix(1463727404, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -167,7 +167,7 @@ func factsCollectorsGet_hostname() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "facts/collectors/get_hostname", size: 227, mode: os.FileMode(493), modTime: time.Unix(1462644938, 0)} + info := bindataFileInfo{name: "facts/collectors/get_hostname", size: 227, mode: os.FileMode(493), modTime: time.Unix(1463727404, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -224,9 +224,9 @@ func AssetNames() []string { // _bindata is a table, holding each asset generator, mapped to its name. var _bindata = map[string]func() (*asset, error){ - "testing/testlets/http": testingTestletsHttp, - "testing/testlets/iperf": testingTestletsIperf, - "testing/testlets/ping": testingTestletsPing, + "testing/bashtestlets/http": testingBashtestletsHttp, + "testing/bashtestlets/iperf": testingBashtestletsIperf, + "testing/bashtestlets/notatallping": testingBashtestletsNotatallping, "facts/collectors/get_addresses": factsCollectorsGet_addresses, "facts/collectors/get_hostname": factsCollectorsGet_hostname, } @@ -278,10 +278,10 @@ var _bintree = &bintree{nil, map[string]*bintree{ }}, }}, "testing": &bintree{nil, map[string]*bintree{ - "testlets": &bintree{nil, map[string]*bintree{ - "http": &bintree{testingTestletsHttp, map[string]*bintree{}}, - "iperf": &bintree{testingTestletsIperf, map[string]*bintree{}}, - "ping": &bintree{testingTestletsPing, map[string]*bintree{}}, + "bashtestlets": &bintree{nil, map[string]*bintree{ + "http": &bintree{testingBashtestletsHttp, map[string]*bintree{}}, + "iperf": &bintree{testingBashtestletsIperf, map[string]*bintree{}}, + "notatallping": &bintree{testingBashtestletsNotatallping, map[string]*bintree{}}, }}, }}, }} diff --git a/cmd/todd-agent/main.go b/cmd/todd-agent/main.go index 30950d9..c70e208 100644 --- a/cmd/todd-agent/main.go +++ b/cmd/todd-agent/main.go @@ -25,7 +25,7 @@ import ( "github.com/Mierdin/todd/hostresources" // Testlet imports (importing these packages registers the testlets) - _ "github.com/Mierdin/todd/agent/testing/testlets/ping" + _ "github.com/Mierdin/todd/agent/testing/downloaded_testlets" ) // Command-line Arguments diff --git a/cmd/todd-server/assets.go b/cmd/todd-server/assets.go index fc649c1..6ff9695 100644 --- a/cmd/todd-server/assets.go +++ b/cmd/todd-server/assets.go @@ -32,7 +32,7 @@ func serveAssets(cfg config.Config) map[string]map[string]string { // Initialize asset map assetMap := map[string]map[string]string{ "factcollectors": hashAssets("facts/collectors"), - "testlets": hashAssets("testing/testlets"), + "testlets": hashAssets("testing/bashtestlets"), } fmt.Println(assetMap) diff --git a/docs/nativetests.rst b/docs/nativetests.rst index e6c04a3..10ec9d2 100644 --- a/docs/nativetests.rst +++ b/docs/nativetests.rst @@ -3,4 +3,18 @@ Native Tests Need to talk about the native tests you've built in, and turn the "testlets" doc into more of a "so you want to build your own, eh?" -Also need to figure out if you want to refer to both native and non-native as "testlets", or maybe reserve that for non-native \ No newline at end of file +Also need to figure out if you want to refer to both native and non-native as "testlets", or maybe reserve that for non-native + + + +Need a design guide outlining some requirements for native testlets: + +* Testlets must honor the "kill" channel passed to the RunTestlet function. If a "true" value is passed into that channel, the testlet must quit immediately. + +* Need to put some specifics together regarding testlets that provide some kind of "server" functionality, kind of like what you've done for custom testlets + +* How do args work in native testlets? It's a bit awkward to continue to use command-line style args in a native testlet but might be necessary to preserve consistency for the user. + +* How to handle vendoring? If a testlet uses a library to run the tests, should the library get vendored with the testlet's repository, or within ToDD proper? Probably the former, but how is it used in that case? Just need to have a strategy here. (You probably vendored such libs in todd proper in order to develop them, so make sure you remove them once they're not needed) + +* How are errors returned from the testlet logic? If a testlet returns an error, how is this handled? \ No newline at end of file diff --git a/scripts/buildtestlets.sh b/scripts/buildtestlets.sh new file mode 100755 index 0000000..2c47c0f --- /dev/null +++ b/scripts/buildtestlets.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +# Copyright 2016 Matt Oswalt. Use or modification of this +# source code is governed by the license provided here: +# https://github.com/mierdin/todd/blob/master/LICENSE + +# This script downloads ToDD testlets prior to compile + +set -e +set -u +set -o pipefail + + +testlets=( + 'https://github.com/Mierdin/todd-nativetestlet-ping.git' + ) + + +rm -rf testlettemp && mkdir testlettemp && cd testlettemp + +for i in "${testlets[@]}" +do + git clone $i +done + +cd .. + +rm -rf agent/testing/downloaded_testlets/ && mkdir agent/testing/downloaded_testlets + +for dir in ./testlettemp/*/ +do + dir=${dir%*/} + cp testlettemp/${dir##*/}/testlet/* agent/testing/downloaded_testlets + #echo ${dir##*/} +done + +rm -rf testlettemp + + + +# rebuild plugins: +# _debug "removing: ${plugin_dir:?}/*" +# rm -rf "${plugin_dir:?}/"* +# mkdir -p "${plugin_dir}" + +# _info "building plugins" +# find "${__proj_dir}/plugin/" -type d -iname "snap-*" -print0 | xargs -0 -n 1 -I{} "${__dir}/build_plugin.sh" {} + +#--------- + + + +# __dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# __proj_dir="$(dirname "$__dir")" + +# # shellcheck source=scripts/common.sh +# . "${__dir}/common.sh" + +# build_dir="${__proj_dir}/build" +# plugin_dir="${build_dir}/plugin" + +# plugin_src_path=$1 +# plugin_name=$(basename "${plugin_src_path}") +# go_build=(go build -a -ldflags "-w") + +# _debug "plugin source: ${plugin_src_path}" +# _info "building ${plugin_name}" + +# (cd "${plugin_src_path}" && "${go_build[@]}" -o "${plugin_dir}/${plugin_name}" . || exit 1) \ No newline at end of file diff --git a/scripts/start-containers.sh b/scripts/start-containers.sh index 05fb8d2..9444333 100755 --- a/scripts/start-containers.sh +++ b/scripts/start-containers.sh @@ -2,7 +2,7 @@ # Copyright 2016 Matt Oswalt. Use or modification of this # source code is governed by the license provided here: -# https://github.com/mierdin/todd:$branch/blob/master/LICENSE +# https://github.com/mierdin/todd/blob/master/LICENSE # This script is designed to manage containers for ToDD. This could be start the basic infrastructure for ToDD like the etcd and rabbitmq containers, # or you could run with the "integration" arg, and run integration tests as well. From 8b6d304d0efa8d9cee1df5d6112d320be844c478 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Sun, 14 Aug 2016 22:56:40 -0700 Subject: [PATCH 010/120] Updating docs Signed-off-by: Matt Oswalt --- docs/customtestlets.rst | 71 +++++++++++++++++++++++++++++++++++ docs/dsl/group-datacenter.yml | 2 +- docs/index.rst | 1 + docs/testlets.rst | 67 +++++++-------------------------- etc/agent.cfg | 2 +- 5 files changed, 87 insertions(+), 56 deletions(-) create mode 100644 docs/customtestlets.rst diff --git a/docs/customtestlets.rst b/docs/customtestlets.rst new file mode 100644 index 0000000..a7023a0 --- /dev/null +++ b/docs/customtestlets.rst @@ -0,0 +1,71 @@ +Custom Testlets +================================ + +ToDD was originally built with no testlets built-in to the agent. All tests were performed using external executable files (i.e. scripts, binaries) that accept a standard set of input, run a test application, and provide a standard set of output containing metrics from that test. Though ToDD has since adopted a number of testlets to be built-in to the agent for simplicity, this functionality still remains, so you can extend ToDD to run whatever types of tests you wish. + +This allows the user to use any testing application (provided it is available on the system on which the ToDD agent is running, and specify which agents run this application. All of the complicated stuff with respect to sending arguments to the underlying testing application as well as parsing the output, is performed inside the testlet. + +.. image:: images/testlet.png + +The testlet is actually run by the ToDD agent, so if there are 3 agents participating in a test, then 3 testlets are running. All logic that performs the test should be contained within the + +Testrun Definition +------------------ + +When you want to run a certain testlet, you refer to it by name. There are a number of testlets built-in to ToDD and are therefore reserved: + +* http +* bandwidth +* ping + +You can, of course, build your own testlet (provided it follows the standard defined on this page) and refer to it by it's filename. + +Check Mode +---------- +Each testlet must support a "check mode". This is a way of running a testlet that allows the ToDD agent to know whether or not a test can be performed, without actually running the test. + +For instance, when the ToDD agent runs the "ping" testlet in check mode, it would invoke it like this: + +.. code-block:: text + + ./testletname check + +That said, the ToDD Server will distribute testrun instructions to the agents in two phases: + +* Install - run the referenced testlet in check mode, and record all params in local agent cache +* Execute - run the installed testrun instruction + +Input +----- +There is little to no similarity between various testing applications with respect to the parameters required by those applications. However, in order to simplify things for the ToDD agent, the testlet - due to it's place as a "wrapper" for a testing application - standardizes this input so the ToDD agent can invoke any testlet in a consistent manner + +.. code-block:: text + + ./testletname < target > < args > + +The ToDD agent will execute the testlet as it exists on the system, and will pass a few arguments to it (meaning the testlet must support and honor these arguments): + +* "target" - this is always the first parameter - represents the IP address or FQDN of the target for this test instance. +* "args" - any arguments required by the underlying application. These should be passed to that application via the testlet + +Output +------ +The output for every testlet is a single-level JSON object, which contains key-value pairs for the metrics gathered for that testlet. + +Since the ToDD agent is responsible for executing a testlet, it is also watching stdout for the testlet to provide this JSON object. This is one of the things that make testlets a very flexible method of performing tests - since it only needs to output these metrics as JSON to stdout, the testlet can be written in any language, as long as they support the arguments described in the "Input" section. + +A sample JSON object that the "ping" testlet will provide is shown below: + +.. code-block:: text + + { + "avg_latency_ms": "27.007", + "packet_loss_percentage": "0" + } + +This specific output covers the metrics for a single testlet run, which means that this is relevant to only a single target, run by a single ToDD agent. The ToDD agent will receive this output once for each target in the testrun, and submit this up to the ToDD server for collection. + +.. NOTE:: + The ToDD Server will also aggregate each agent's report to a single metric document for the entire testrun, so that it's easy to see the metrics for each source-to-target relationship for a testrun. + +The ToDD agent does not have an opinion on the values contained in the keys or values for this JSON object, or how many k/v pairs there are - only that it is valid JSON, and is a single level (no nested objects, lists, etc). \ No newline at end of file diff --git a/docs/dsl/group-datacenter.yml b/docs/dsl/group-datacenter.yml index 1ee95b3..5a2c541 100644 --- a/docs/dsl/group-datacenter.yml +++ b/docs/dsl/group-datacenter.yml @@ -4,4 +4,4 @@ label: datacenter spec: group: datacenter matches: - - within_subnet: "172.17.0.0/16" \ No newline at end of file + - within_subnet: "172.17.0.0/16" diff --git a/docs/index.rst b/docs/index.rst index 1a17e95..e117dca 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -18,6 +18,7 @@ ToDD Documentation objects.rst resources.rst testlets.rst + customtestlets.rst This is ToDD's documentation. The docs are a work in progress, but check back soon! I'll be updating this heavily over the next few days and weeks. diff --git a/docs/testlets.rst b/docs/testlets.rst index 06afe4d..740690b 100644 --- a/docs/testlets.rst +++ b/docs/testlets.rst @@ -1,71 +1,30 @@ -ToDD Testlets +Testlets ================================ -Tests in ToDD are powered by something called "testlets". These are executable files (i.e. scripts, binaries) that accept a standard set of input, run a test application, and provide a standard set of output containing metrics from that test. +Testing applications are referred to "testlets" in ToDD. This is a handy way of referring to "whatever is actually doing the work". ToDD simply orchestrates this work. -This allows the user to simply "use" this application, and specify which agents run this application. All of the complicated stuff with respect to sending arguments to the underlying testing application as well as parsing the output, is performed inside the testlet. - -.. image:: images/testlet.png - -The testlet is actually run by the ToDD agent, so if there are 3 agents participating in a test, then 3 testlets are running. All logic that performs the test should be contained within the - -Testrun Definition ------------------- - -When you want to run a certain testlet, you refer to it by name. There are a number of testlets built in to ToDD and are therefore reserved: +There are a number of testlets built-in to the ToDD agent and are usable simply by installing the agent on a system: * http * bandwidth * ping -If you don't want to use any of the built-in testlets, you can, of course, build your own testlet (provided it follows the standard defined on this page) and refer to it by it's filename. - -Check Mode ----------- -Each testlet must support a "check mode". This is a way of running a testlet that allows the ToDD agent to know whether or not a test can be performed, without actually running the test. - -For instance, when the ToDD agent runs the "ping" testlet in check mode, it would invoke it like this: - -.. code-block:: text - - ./testletname check - -That said, the ToDD Server will distribute testrun instructions to the agents in two phases: - -* Install - run the referenced testlet in check mode, and record all params in local agent cache -* Execute - run the installed testrun instruction - -Input ------ -There is little to no similarity between various testing applications with respect to the parameters required by those applications. However, in order to simplify things for the ToDD agent, the testlet - due to it's place as a "wrapper" for a testing application - standardizes this input so the ToDD agent can invoke any testlet in a consistent manner - -.. code-block:: text - - ./testletname < target > < args > - -The ToDD agent will execute the testlet as it exists on the system, and will pass a few arguments to it (meaning the testlet must support and honor these arguments): +However, please see "Custom Testlets", and you'll find it's quite easy to build your own testlets and run them with ToDD. This extensibility was a core design principle of ToDD since the beginning of the project. -* "target" - this is always the first parameter - represents the IP address or FQDN of the target for this test instance. -* "args" - any arguments required by the underlying application. These should be passed to that application via the testlet -Output ------- -The output for every testlet is a single-level JSON object, which contains key-value pairs for the metrics gathered for that testlet. +Native Testlet Design Principles +-------------------------------- -Since the ToDD agent is responsible for executing a testlet, it is also watching stdout for the testlet to provide this JSON object. This is one of the things that make testlets a very flexible method of performing tests - since it only needs to output these metrics as JSON to stdout, the testlet can be written in any language, as long as they support the arguments described in the "Input" section. +Need a design guide outlining some requirements for native testlets: -A sample JSON object that the "ping" testlet will provide is shown below: +* Testlets must honor the "kill" channel passed to the RunTestlet function. If a "true" value is passed into that channel, the testlet must quit immediately. -.. code-block:: text +* Need to put some specifics together regarding testlets that provide some kind of "server" functionality, kind of like what you've done for custom testlets - { - "avg_latency_ms": "27.007", - "packet_loss_percentage": "0" - } +* How do args work in native testlets? It's a bit awkward to continue to use command-line style args in a native testlet but might be necessary to preserve consistency for the user. -This specific output covers the metrics for a single testlet run, which means that this is relevant to only a single target, run by a single ToDD agent. The ToDD agent will receive this output once for each target in the testrun, and submit this up to the ToDD server for collection. +* How to handle vendoring? If a testlet uses a library to run the tests, should the library get vendored with the testlet's repository, or within ToDD proper? Probably the former, but how is it used in that case? Just need to have a strategy here. (You probably vendored such libs in todd proper in order to develop them, so make sure you remove them once they're not needed) -.. NOTE:: - The ToDD Server will also aggregate each agent's report to a single metric document for the entire testrun, so that it's easy to see the metrics for each source-to-target relationship for a testrun. +* How are errors returned from the testlet logic? If a testlet returns an error, how is this handled? -The ToDD agent does not have an opinion on the values contained in the keys or values for this JSON object, or how many k/v pairs there are - only that it is valid JSON, and is a single level (no nested objects, lists, etc). \ No newline at end of file +* How does development work? Do you clone the testlet repo next to the todd repo, kind of like devstack? \ No newline at end of file diff --git a/etc/agent.cfg b/etc/agent.cfg index cfdfbdf..cca07b0 100644 --- a/etc/agent.cfg +++ b/etc/agent.cfg @@ -6,6 +6,6 @@ Port = 5672 Plugin = rabbitmq [LocalResources] -DefaultInterface = eth0 +DefaultInterface = eth2 # IPAddrOverride = 192.168.99.100 # Normally, the DefaultInterface configuration option is used to get IP address. This overrides that in the event that it doesn't work OptDir = /opt/todd/agent From 412423ae56f10c43035d550d88b880530ffcde36 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Fri, 19 Aug 2016 12:00:41 -0700 Subject: [PATCH 011/120] Updates to vagrant and make Signed-off-by: Matt Oswalt --- Makefile | 5 +++++ Vagrantfile | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 8935ba5..9f07c9f 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,8 @@ build: docker build -t mierdin/todd -f Dockerfile . compile: + # Need to support some kind of mode that allows for development (i.e. not downloading the testlets live, but rather from a directory?) + # eval ./scripts/buildtestlets.sh && go install ./cmd/... ./scripts/buildtestlets.sh go install ./cmd/... @@ -38,3 +40,6 @@ configureenv: if ! [ "etc" -ef "/etc/todd" ]; then mkdir -p /etc/todd && cp -f ./etc/{agent,server}.cfg /etc/todd/; fi mkdir -p /opt/todd/{agent,server}/assets/{factcollectors,testlets} chmod -R 777 /opt/todd + + # If on Linux, enable ping testlet functionality + sudo sysctl -w net.ipv4.ping_group_range="0 12345" diff --git a/Vagrantfile b/Vagrantfile index 9317dee..a9efd37 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -6,7 +6,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.box = "trusty64" config.vm.box_url = "http://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-amd64-vagrant-disk1.box" - config.vm.synced_folder '.', '/home/vagrant/go/src/github.com/Mierdin/todd', nfs: true + config.vm.synced_folder '../../', '/home/vagrant/go/src/github.com', nfs: true config.vm.provider "virtualbox" do |v| From 7909ab4a2e7280ead16f18cfb576ffe22d6d7ae2 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Fri, 19 Aug 2016 12:03:45 -0700 Subject: [PATCH 012/120] Catch up Signed-off-by: Matt Oswalt --- cmd/todd-agent/main.go | 5 ++-- docs/dsl/branch-to-dc-bw.yml | 14 +++++++++++ docs/dsl/group-branch.yml | 7 ++++++ docs/dsl/group-uraj.yml | 7 ++++++ docs/nativetests.rst | 20 ---------------- docs/roadmap.rst | 17 +++++++++++++ docs/testlets.rst | 9 +++++++ scripts/buildtestlets.sh | 46 ++++++++++++++++++++++++++---------- 8 files changed, 91 insertions(+), 34 deletions(-) create mode 100644 docs/dsl/branch-to-dc-bw.yml create mode 100644 docs/dsl/group-branch.yml create mode 100644 docs/dsl/group-uraj.yml delete mode 100644 docs/nativetests.rst create mode 100644 docs/roadmap.rst diff --git a/cmd/todd-agent/main.go b/cmd/todd-agent/main.go index c70e208..880efc7 100644 --- a/cmd/todd-agent/main.go +++ b/cmd/todd-agent/main.go @@ -25,8 +25,9 @@ import ( "github.com/Mierdin/todd/hostresources" // Testlet imports (importing these packages registers the testlets) - _ "github.com/Mierdin/todd/agent/testing/downloaded_testlets" -) + // Need to make this dynamic if possible (at compile time of course) + // TODO Not necessary anymore + //_ "github.com/toddproject/todd-nativetestlet-ping/testlet" // Command-line Arguments var arg_config string diff --git a/docs/dsl/branch-to-dc-bw.yml b/docs/dsl/branch-to-dc-bw.yml new file mode 100644 index 0000000..b1683a4 --- /dev/null +++ b/docs/dsl/branch-to-dc-bw.yml @@ -0,0 +1,14 @@ +--- +# Example test file +type: testrun +label: branch-to-dc-bw +spec: + targettype: group + source: + name: branch-uraj + app: iperf + args: "-c {{ target }}" + target: + name: datacenter + app: iperf + args: "-s" diff --git a/docs/dsl/group-branch.yml b/docs/dsl/group-branch.yml new file mode 100644 index 0000000..b1d1e82 --- /dev/null +++ b/docs/dsl/group-branch.yml @@ -0,0 +1,7 @@ +--- +type: group +label: branch-uraj +spec: + group: branch-uraj + matches: + - hostname: "uraj" diff --git a/docs/dsl/group-uraj.yml b/docs/dsl/group-uraj.yml new file mode 100644 index 0000000..b1d1e82 --- /dev/null +++ b/docs/dsl/group-uraj.yml @@ -0,0 +1,7 @@ +--- +type: group +label: branch-uraj +spec: + group: branch-uraj + matches: + - hostname: "uraj" diff --git a/docs/nativetests.rst b/docs/nativetests.rst deleted file mode 100644 index 10ec9d2..0000000 --- a/docs/nativetests.rst +++ /dev/null @@ -1,20 +0,0 @@ -Native Tests -================================ - -Need to talk about the native tests you've built in, and turn the "testlets" doc into more of a "so you want to build your own, eh?" - -Also need to figure out if you want to refer to both native and non-native as "testlets", or maybe reserve that for non-native - - - -Need a design guide outlining some requirements for native testlets: - -* Testlets must honor the "kill" channel passed to the RunTestlet function. If a "true" value is passed into that channel, the testlet must quit immediately. - -* Need to put some specifics together regarding testlets that provide some kind of "server" functionality, kind of like what you've done for custom testlets - -* How do args work in native testlets? It's a bit awkward to continue to use command-line style args in a native testlet but might be necessary to preserve consistency for the user. - -* How to handle vendoring? If a testlet uses a library to run the tests, should the library get vendored with the testlet's repository, or within ToDD proper? Probably the former, but how is it used in that case? Just need to have a strategy here. (You probably vendored such libs in todd proper in order to develop them, so make sure you remove them once they're not needed) - -* How are errors returned from the testlet logic? If a testlet returns an error, how is this handled? \ No newline at end of file diff --git a/docs/roadmap.rst b/docs/roadmap.rst new file mode 100644 index 0000000..9c9be29 --- /dev/null +++ b/docs/roadmap.rst @@ -0,0 +1,17 @@ +Roadmap +================================ + +Goals for Alpha Release + +* Store group-to-group aggreagate test data as a graph (agent groups are the lowest addressable node in teh graph) +* Consider getting rid of YAML files. It should be easy to run tests using a one-liner (native testlets should have reasonable defaults) or they just run on their own in the background +* Simplify agent setup. Is there a way to provide configuration-less agent setup? +* Need to look at modifying ToDD to run tests in a hands-off way. Think about the inspiration from the vendors at NFD12. Need to rethink the execution of tests. You know you can store test results without server connectivity, but can you run tests without server connectivity? + +Goals for Beta Release + +* Goal 1 + +Goals for Full Release + +* Web Front-End? \ No newline at end of file diff --git a/docs/testlets.rst b/docs/testlets.rst index 740690b..092742d 100644 --- a/docs/testlets.rst +++ b/docs/testlets.rst @@ -8,6 +8,9 @@ There are a number of testlets built-in to the ToDD agent and are usable simply * http * bandwidth * ping +* portknock + +These have their own separate repositories and are distributed alongside ToDD proper. They are written in Go for a number of reasons. First, it makes it easy for the testlets to honor the testlet format by leveraging some common code in the ToDD repository. However, the testlets are still their own binary. In addition, it allows ToDD to execute tests consistently across platforms (The old model of using bash scripts meant the tests had to be run on a certain platform for which that testlet knew how to parse the output) However, please see "Custom Testlets", and you'll find it's quite easy to build your own testlets and run them with ToDD. This extensibility was a core design principle of ToDD since the beginning of the project. @@ -15,6 +18,12 @@ However, please see "Custom Testlets", and you'll find it's quite easy to build Native Testlet Design Principles -------------------------------- + +Need to talk about the native tests you've built in, and turn the "testlets" doc into more of a "so you want to build your own, eh?" + +Also need to figure out if you want to refer to both native and non-native as "testlets", or maybe reserve that for non-native + + Need a design guide outlining some requirements for native testlets: * Testlets must honor the "kill" channel passed to the RunTestlet function. If a "true" value is passed into that channel, the testlet must quit immediately. diff --git a/scripts/buildtestlets.sh b/scripts/buildtestlets.sh index 2c47c0f..0875fb1 100755 --- a/scripts/buildtestlets.sh +++ b/scripts/buildtestlets.sh @@ -12,29 +12,51 @@ set -o pipefail testlets=( - 'https://github.com/Mierdin/todd-nativetestlet-ping.git' + 'github.com/toddproject/todd-nativetestlet-ping' ) -rm -rf testlettemp && mkdir testlettemp && cd testlettemp +#rm -rf testlettemp && mkdir testlettemp && cd testlettemp for i in "${testlets[@]}" do - git clone $i + #echo "Installing $i" + # git clone $i --quiet + go get -d -u $i/... done -cd .. +# cd .. + +# rm -rf agent/testing/downloaded_testlets/ && mkdir agent/testing/downloaded_testlets + +# for dir in ./testlettemp/*/ +# do +# dir=${dir%*/} +# cp testlettemp/${dir##*/}/testlet/* agent/testing/downloaded_testlets +# #echo ${dir##*/} + + + +# testletdir="$(pwd)/$dir" +# #echo $testletdir + +# ln -s $testletdir/vendor/ $testletdir/vendor/src + +# # echo ./testlettemp/todd-nativetestlet-ping + +# # Append this vendor directory to GOPATH +# # TODO need to do some cleanup somewhere to remove this +# if [[ ":$GOPATH:" != *":$testletdir/vendor:"* ]]; then +# echo "export GOPATH=$GOPATH:$testletdir/vendor" +# fi + + +# done + -rm -rf agent/testing/downloaded_testlets/ && mkdir agent/testing/downloaded_testlets -for dir in ./testlettemp/*/ -do - dir=${dir%*/} - cp testlettemp/${dir##*/}/testlet/* agent/testing/downloaded_testlets - #echo ${dir##*/} -done -rm -rf testlettemp +# rm -rf testlettemp From aec7dd296aceffde78d18b9361023ff1c8925eb7 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Fri, 19 Aug 2016 14:52:22 -0700 Subject: [PATCH 013/120] Got native testlets working Signed-off-by: Matt Oswalt --- agent/tasks/executetestrun.go | 176 +++++++++++++++++------------ agent/tasks/installtestrun.go | 55 ++++----- agent/testing/testlets/testlets.go | 19 ++-- cmd/todd-agent/main.go | 2 +- 4 files changed, 143 insertions(+), 109 deletions(-) diff --git a/agent/tasks/executetestrun.go b/agent/tasks/executetestrun.go index b9210de..182a14a 100644 --- a/agent/tasks/executetestrun.go +++ b/agent/tasks/executetestrun.go @@ -69,94 +69,124 @@ func (ett ExecuteTestRunTask) Run() error { // Specify size of wait group wg.Add(len(tr.Targets)) + //native := false + + // for old, newname := range testlets.nativeTestlets { + // if tr.Testlet == old { + // tr.Testlet = newname + // native = true + // break + // } + // } + + // log.Error(tr.Testlet) + // log.Error(testlets.IsNativeTestlet(tr.Testlet)) + + var testlet_path string + //if testlets.IsNativeTestlet(tr.Testlet) { + isNative, newTestletName := testlets.IsNativeTestlet(tr.Testlet) + log.Error("POOP") + log.Error(isNative) + log.Error(newTestletName) + if isNative { + + log.Error("POOP2") + + tr.Testlet = newTestletName + + log.Error(tr.Testlet) + + // Generate path to testlet and make sure it exists. + testlet_path = fmt.Sprintf("%s", tr.Testlet) + // if _, err := os.Stat(testlet_path); os.IsNotExist(err) { + // log.Error(err) + // log.Errorf("Testlet %s does not exist on this agent", tr.Testlet) + // return errors.New("Error executing testrun - testlet doesn't exist on this agent.") + // } + } else { + // Generate path to testlet and make sure it exists. + testlet_path = fmt.Sprintf("%s/assets/testlets/%s", ett.Config.LocalResources.OptDir, tr.Testlet) + if _, err := os.Stat(testlet_path); os.IsNotExist(err) { + log.Errorf("Testlet %s does not exist on this agent", tr.Testlet) + return errors.New("Error executing testrun - testlet doesn't exist on this agent.") + } + } + // Execute testlets against all targets asynchronously for i := range tr.Targets { thisTarget := tr.Targets[i] - if testlets.IsNativeTestlet(tr.Testlet) { - go func() { + go func() { - // TODO(mierdin): Something worried me here (can't remember what) regarding - // if only some agents were running native testlets, does this wait group methodology work? - // Sorry it's not clear, I have had a bit too much wine. - defer wg.Done() + defer wg.Done() - nativeTestlet, err := testlets.NewTestlet(tr.Testlet) - if err != nil { - //TODO(mierdin) do something - } - - metrics, err := nativeTestlet.Run("8.8.8.8", []string{"-c 10", "-s"}, ett.TimeLimit) - //log.Error(nativeTestlet.RunFunction) - if err != nil { - log.Errorf("Testlet completed with error '%s'", err) - gatheredData[thisTarget] = "error" - } + log.Debugf("Full testlet command and args: '%s %s %s'", tr.Testlet, thisTarget, tr.Args) + cmd := exec.Command(tr.Testlet, thisTarget, tr.Args) - // The metrics infrastructure requires that we collect metrics as a JSON string - // (which is a result of building non-native testlets in early versions of ToDD) - // So let's convert, and add to gatheredData - metrics_json, err := json.Marshal(metrics) - if err != nil { - //TODO(mierdin) do something - } - gatheredData[thisTarget] = string(metrics_json) + // Stdout buffer + cmdOutput := &bytes.Buffer{} + // Attach buffer to command + cmd.Stdout = cmdOutput - }() - } else { - // Generate path to testlet and make sure it exists. - testlet_path := fmt.Sprintf("%s/assets/testlets/%s", ett.Config.LocalResources.OptDir, tr.Testlet) - if _, err := os.Stat(testlet_path); os.IsNotExist(err) { - log.Errorf("Testlet %s does not exist on this agent", tr.Testlet) - return errors.New("Error executing testrun - testlet doesn't exist on this agent.") - } + // Execute collector + cmd.Start() + // TODO(mierdin): Does this need to be a buffered channel? + done := make(chan error, 1) go func() { + done <- cmd.Wait() + }() - defer wg.Done() - - log.Debugf("Full testlet command and args: '%s %s %s'", testlet_path, thisTarget, tr.Args) - cmd := exec.Command(testlet_path, thisTarget, tr.Args) - - // Stdout buffer - cmdOutput := &bytes.Buffer{} - // Attach buffer to command - cmd.Stdout = cmdOutput - - // Execute collector - cmd.Start() - - // TODO(mierdin): Does this need to be a buffered channel? - done := make(chan error, 1) - go func() { - done <- cmd.Wait() - }() - - // This select statement will block until one of these two conditions are met: - // - The testlet finishes, in which case the channel "done" will be receive a value - // - The configured time limit is exceeded (expected for testlets running in server mode) - select { - case <-time.After(time.Duration(ett.TimeLimit) * time.Second): - if err := cmd.Process.Kill(); err != nil { - log.Errorf("Failed to kill %s after timeout: %s", testlet_path, err) - } else { - log.Debug("Successfully killed ", testlet_path) - } - case err := <-done: - if err != nil { - log.Errorf("Testlet %s completed with error '%s'", testlet_path, err) - gatheredData[thisTarget] = "error" - } else { - log.Debugf("Testlet %s completed without error", testlet_path) - } + // This select statement will block until one of these two conditions are met: + // - The testlet finishes, in which case the channel "done" will be receive a value + // - The configured time limit is exceeded (expected for testlets running in server mode) + select { + case <-time.After(time.Duration(ett.TimeLimit) * time.Second): + if err := cmd.Process.Kill(); err != nil { + log.Errorf("Failed to kill %s after timeout: %s", testlet_path, err) + } else { + log.Debug("Successfully killed ", testlet_path) } + case err := <-done: + if err != nil { + log.Errorf("Testlet %s completed with error '%s'", testlet_path, err) + gatheredData[thisTarget] = "error" + } else { + log.Debugf("Testlet %s completed without error", testlet_path) + } + } - // Record test data - gatheredData[thisTarget] = string(cmdOutput.Bytes()) + // Record test data + gatheredData[thisTarget] = string(cmdOutput.Bytes()) + // // TODO(mierdin): Something worried me here (can't remember what) regarding + // // if only some agents were running native testlets, does this wait group methodology work? + // // Sorry it's not clear, I have had a bit too much wine. + // defer wg.Done() + + // nativeTestlet, err := testlets.NewTestlet(tr.Testlet) + // if err != nil { + // //TODO(mierdin) do something + // } + + // metrics, err := nativeTestlet.Run("8.8.8.8", []string{"-c 10", "-s"}, ett.TimeLimit) + // //log.Error(nativeTestlet.RunFunction) + // if err != nil { + // log.Errorf("Testlet completed with error '%s'", err) + // gatheredData[thisTarget] = "error" + // } + + // // The metrics infrastructure requires that we collect metrics as a JSON string + // // (which is a result of building non-native testlets in early versions of ToDD) + // // So let's convert, and add to gatheredData + // metrics_json, err := json.Marshal(metrics) + // if err != nil { + // //TODO(mierdin) do something + // } + // gatheredData[thisTarget] = string(metrics_json) + + }() - }() - } } wg.Wait() diff --git a/agent/tasks/installtestrun.go b/agent/tasks/installtestrun.go index 131df4d..96bb662 100644 --- a/agent/tasks/installtestrun.go +++ b/agent/tasks/installtestrun.go @@ -43,48 +43,49 @@ func (itt InstallTestRunTask) Run() error { return errors.New("Testlet parameter for this testrun is null") } - // Determine if this is a valid native testlet - _, err := testlets.NewTestlet(itt.Tr.Testlet) + var testlet_path string + // Determine if this is a valid native testlet + //_, err := testlets.NewTestlet(itt.Tr.Testlet) + isNative, newName := testlets.IsNativeTestlet(itt.Tr.Testlet) // Not a native testlet - attempt to run check mode on testlet in filesystem - if err != nil { + if isNative { + // Nothing to do, as we're using a native testlet + log.Infof("%s is a native testlet - installing testrun.", itt.Tr.Testlet) + //itt.Tr.Testlet = newName + testlet_path = newName + } else { // Generate path to testlet and make sure it exists. - testlet_path := fmt.Sprintf("%s/assets/testlets/%s", itt.Config.LocalResources.OptDir, itt.Tr.Testlet) + testlet_path = fmt.Sprintf("%s/assets/testlets/%s", itt.Config.LocalResources.OptDir, itt.Tr.Testlet) if _, err := os.Stat(testlet_path); os.IsNotExist(err) { log.Errorf("Testlet %s does not exist on this agent", itt.Tr.Testlet) return errors.New("Error installing testrun - testlet doesn't exist on this agent.") } + } - // Run the testlet in check mode to verify that everything is okay to run this test - log.Debug("Running testlet in check mode: ", testlet_path) - cmd := exec.Command(testlet_path, "check") - - // Stdout buffer - cmdOutput := &bytes.Buffer{} - // Attach buffer to command - cmd.Stdout = cmdOutput - // Execute collector - cmd.Run() - - // This is probably the best cross-platform way to see if check mode passed. - if strings.Contains(string(cmdOutput.Bytes()), "Check mode PASSED") { - log.Debugf("Check mode for %s passed", testlet_path) - } else { - log.Error("Testlet returned an error during check mode: ", string(cmdOutput.Bytes())) - return errors.New("Testlet returned an error during check mode") - } - - } else { + // Run the testlet in check mode to verify that everything is okay to run this test + log.Debug("Running testlet in check mode: ", testlet_path) + cmd := exec.Command(testlet_path, "check") - // Nothing to do, as we're using a native testlet - log.Infof("%s is a native testlet - installing testrun.", itt.Tr.Testlet) + // Stdout buffer + cmdOutput := &bytes.Buffer{} + // Attach buffer to command + cmd.Stdout = cmdOutput + // Execute collector + cmd.Run() + // This is probably the best cross-platform way to see if check mode passed. + if strings.Contains(string(cmdOutput.Bytes()), "Check mode PASSED") { + log.Debugf("Check mode for %s passed", testlet_path) + } else { + log.Error("Testlet returned an error during check mode: ", string(cmdOutput.Bytes())) + return errors.New("Testlet returned an error during check mode") } // Insert testrun into agent cache var ac = cache.NewAgentCache(itt.Config) - err = ac.InsertTestRun(itt.Tr) + err := ac.InsertTestRun(itt.Tr) if err != nil { log.Error(err) return errors.New("Problem installing test run into agent cache") diff --git a/agent/testing/testlets/testlets.go b/agent/testing/testlets/testlets.go index 4458892..7bcafe1 100644 --- a/agent/testing/testlets/testlets.go +++ b/agent/testing/testlets/testlets.go @@ -12,10 +12,13 @@ import ( ) var ( - testletsMu sync.RWMutex - testlets = make(map[string]Testlet) - done = make(chan error) // Used from within the goroutine to inform the infrastructure it has finished - kill = make(chan bool) // Used from outside the goroutine to inform the goroutine to stop + testletsMu sync.RWMutex + testlets = make(map[string]Testlet) + done = make(chan error) // Used from within the goroutine to inform the infrastructure it has finished + kill = make(chan bool) // Used from outside the goroutine to inform the goroutine to stop + nativeTestlets = map[string]string{ + "ping": "toddping", + } ) // Testlet defines what a testlet should look like if built in native @@ -107,11 +110,11 @@ func (b BaseTestlet) Kill() error { // IsNativeTestlet polls the list of registered native testlets, and returns // true if the referenced name exists -func IsNativeTestlet(name string) bool { - if _, ok := testlets[name]; ok { - return true +func IsNativeTestlet(name string) (bool, string) { + if _, ok := nativeTestlets[name]; ok { + return true, nativeTestlets[name] } else { - return false + return false, "" } } diff --git a/cmd/todd-agent/main.go b/cmd/todd-agent/main.go index 880efc7..a7f527e 100644 --- a/cmd/todd-agent/main.go +++ b/cmd/todd-agent/main.go @@ -23,11 +23,11 @@ import ( "github.com/Mierdin/todd/comms" "github.com/Mierdin/todd/config" "github.com/Mierdin/todd/hostresources" - // Testlet imports (importing these packages registers the testlets) // Need to make this dynamic if possible (at compile time of course) // TODO Not necessary anymore //_ "github.com/toddproject/todd-nativetestlet-ping/testlet" +) // Command-line Arguments var arg_config string From c09854c5b82f6008bff911a88517d07869b2bb96 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Mon, 22 Aug 2016 23:39:44 -0700 Subject: [PATCH 014/120] Catch up Signed-off-by: Matt Oswalt --- Makefile | 9 ++- agent/tasks/executetestrun.go | 79 ++++----------------- agent/tasks/installtestrun.go | 11 ++- agent/testing/bashtestlets/notatallping | 32 --------- agent/testing/testlets/testlets.go | 34 +++++++-- scripts/buildtestlets.sh | 91 ------------------------- scripts/gettestlets.sh | 23 +++++++ 7 files changed, 77 insertions(+), 202 deletions(-) delete mode 100755 agent/testing/bashtestlets/notatallping delete mode 100755 scripts/buildtestlets.sh create mode 100755 scripts/gettestlets.sh diff --git a/Makefile b/Makefile index 9f07c9f..f994d9a 100644 --- a/Makefile +++ b/Makefile @@ -11,9 +11,12 @@ build: docker build -t mierdin/todd -f Dockerfile . compile: - # Need to support some kind of mode that allows for development (i.e. not downloading the testlets live, but rather from a directory?) - # eval ./scripts/buildtestlets.sh && go install ./cmd/... - ./scripts/buildtestlets.sh + # TODO(mierdin): Need to support some kind of mode that allows for development (i.e. not downloading the testlets live, but rather from a directory?) + + # Installing testlets + ./scripts/gettestlets.sh + + # Installing ToDD go install ./cmd/... install: configureenv diff --git a/agent/tasks/executetestrun.go b/agent/tasks/executetestrun.go index 182a14a..de1ebbc 100644 --- a/agent/tasks/executetestrun.go +++ b/agent/tasks/executetestrun.go @@ -52,8 +52,9 @@ func (ett ExecuteTestRunTask) Run() error { gatheredData = map[string]string{} // Waiting three seconds to ensure all the agents have their tasks before we potentially hammer the network - // TODO(mierdin): This is a bit of a copout. I would like to do something a little more robust than simply waiting - // for a few seconds in the future. + // TODO(mierdin): This is a temporary measure - in the future, testruns will be executed via time schedule, + // making not only this sleep, but also the entire task unnecessary. Testruns will simply be installed, and + // executed when the time is right. time.Sleep(3000 * time.Millisecond) // Retrieve test from cache by UUID @@ -66,52 +67,29 @@ func (ett ExecuteTestRunTask) Run() error { log.Debugf("IMMA FIRIN MAH LAZER (for test %s) ", ett.TestUuid) - // Specify size of wait group + // Specify size of wait group equal to number of targets wg.Add(len(tr.Targets)) - //native := false - - // for old, newname := range testlets.nativeTestlets { - // if tr.Testlet == old { - // tr.Testlet = newname - // native = true - // break - // } - // } - - // log.Error(tr.Testlet) - // log.Error(testlets.IsNativeTestlet(tr.Testlet)) - var testlet_path string - //if testlets.IsNativeTestlet(tr.Testlet) { isNative, newTestletName := testlets.IsNativeTestlet(tr.Testlet) - log.Error("POOP") - log.Error(isNative) - log.Error(newTestletName) - if isNative { - - log.Error("POOP2") - - tr.Testlet = newTestletName - log.Error(tr.Testlet) - - // Generate path to testlet and make sure it exists. - testlet_path = fmt.Sprintf("%s", tr.Testlet) - // if _, err := os.Stat(testlet_path); os.IsNotExist(err) { - // log.Error(err) - // log.Errorf("Testlet %s does not exist on this agent", tr.Testlet) - // return errors.New("Error executing testrun - testlet doesn't exist on this agent.") - // } + // If we're running a native testlet, we want testlet_path to simply be the testlet name + // (since it is a requirement that the native-Go testlets are in the PATH) + // If the testlet is not native, we can get the full path. + if isNative { + testlet_path = newTestletName } else { // Generate path to testlet and make sure it exists. testlet_path = fmt.Sprintf("%s/assets/testlets/%s", ett.Config.LocalResources.OptDir, tr.Testlet) if _, err := os.Stat(testlet_path); os.IsNotExist(err) { - log.Errorf("Testlet %s does not exist on this agent", tr.Testlet) + log.Errorf("Testlet %s does not exist on this agent", testlet_path) return errors.New("Error executing testrun - testlet doesn't exist on this agent.") } } + // TODO(mierdin): What about testlets running as servers (i.e. 'iperf -s')? Are we spinning up len(tr.Targets) + // number of those? + // Execute testlets against all targets asynchronously for i := range tr.Targets { @@ -121,8 +99,8 @@ func (ett ExecuteTestRunTask) Run() error { defer wg.Done() - log.Debugf("Full testlet command and args: '%s %s %s'", tr.Testlet, thisTarget, tr.Args) - cmd := exec.Command(tr.Testlet, thisTarget, tr.Args) + log.Debugf("Full testlet command and args: '%s %s %s'", testlet_path, thisTarget, tr.Args) + cmd := exec.Command(testlet_path, thisTarget, tr.Args) // Stdout buffer cmdOutput := &bytes.Buffer{} @@ -159,34 +137,7 @@ func (ett ExecuteTestRunTask) Run() error { // Record test data gatheredData[thisTarget] = string(cmdOutput.Bytes()) - // // TODO(mierdin): Something worried me here (can't remember what) regarding - // // if only some agents were running native testlets, does this wait group methodology work? - // // Sorry it's not clear, I have had a bit too much wine. - // defer wg.Done() - - // nativeTestlet, err := testlets.NewTestlet(tr.Testlet) - // if err != nil { - // //TODO(mierdin) do something - // } - - // metrics, err := nativeTestlet.Run("8.8.8.8", []string{"-c 10", "-s"}, ett.TimeLimit) - // //log.Error(nativeTestlet.RunFunction) - // if err != nil { - // log.Errorf("Testlet completed with error '%s'", err) - // gatheredData[thisTarget] = "error" - // } - - // // The metrics infrastructure requires that we collect metrics as a JSON string - // // (which is a result of building non-native testlets in early versions of ToDD) - // // So let's convert, and add to gatheredData - // metrics_json, err := json.Marshal(metrics) - // if err != nil { - // //TODO(mierdin) do something - // } - // gatheredData[thisTarget] = string(metrics_json) - }() - } wg.Wait() diff --git a/agent/tasks/installtestrun.go b/agent/tasks/installtestrun.go index 96bb662..355e45e 100644 --- a/agent/tasks/installtestrun.go +++ b/agent/tasks/installtestrun.go @@ -45,16 +45,15 @@ func (itt InstallTestRunTask) Run() error { var testlet_path string - // Determine if this is a valid native testlet - //_, err := testlets.NewTestlet(itt.Tr.Testlet) + // Determine if this is a native testlet isNative, newName := testlets.IsNativeTestlet(itt.Tr.Testlet) - // Not a native testlet - attempt to run check mode on testlet in filesystem + + // If we're running a native testlet, we want testlet_path to simply be the testlet name + // (since it is a requirement that the native-Go testlets are in the PATH) + // If the testlet is not native, we can get the full path. if isNative { - // Nothing to do, as we're using a native testlet log.Infof("%s is a native testlet - installing testrun.", itt.Tr.Testlet) - //itt.Tr.Testlet = newName testlet_path = newName - } else { // Generate path to testlet and make sure it exists. testlet_path = fmt.Sprintf("%s/assets/testlets/%s", itt.Config.LocalResources.OptDir, itt.Tr.Testlet) diff --git a/agent/testing/bashtestlets/notatallping b/agent/testing/bashtestlets/notatallping deleted file mode 100755 index aed17d4..0000000 --- a/agent/testing/bashtestlets/notatallping +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env bash - -# Copyright 2016 Matt Oswalt. Use or modification of this -# source code is governed by the license provided here: -# https://github.com/Mierdin/todd/blob/master/LICENSE - -check() { - echo "Running in check mode" - command -v ping >/dev/null 2>&1 || { echo >&2 "Ping is not installed. Exiting."; exit 1; } - echo "Check mode PASSED" - exit 0 -} - -if [[ $1 == "check" ]]; - then - check -fi - -# The first argument after the testlet name will ALWAYS be the target. So, $1 is always the target, and all arguments from $2 and up should be passed into the app command -app_args="$@" -app_args=${app_args#$0 } -app_args=${app_args#$1 } - -app_run_command="ping $app_args $1" - -output=$($app_run_command) - -packet_loss=$(echo $output | tr '\n' ' ' | sed -n 's/.* \(.*\)% packet loss.*/\1/p') -avg_latency=$(echo $output | tr '\n' ' ' | sed -n 's/.*dev = .*\/\(.*\)\/.*\/.*/\1/p') - -teslet_data='{"packet_loss_percentage":"'$packet_loss'", "avg_latency_ms":"'$avg_latency'"}' -echo $teslet_data \ No newline at end of file diff --git a/agent/testing/testlets/testlets.go b/agent/testing/testlets/testlets.go index 7bcafe1..ea35bce 100644 --- a/agent/testing/testlets/testlets.go +++ b/agent/testing/testlets/testlets.go @@ -6,16 +6,37 @@ import ( "sort" "sync" "time" - //"sync/atomic" log "github.com/Sirupsen/logrus" ) +// NOTE +// +// Early efforts to build native-Go testlets involved the embedding of testlet logic into the +// ToDD agent itself. As a result, it was important to build some reusable infrastructure so that goroutines +// running testlet code inside the agent could be controlled, and that new testlets could benefit from this +// infrastructure. +// +// Since then, the decision was made to keep testlets as their own separate binaries. Despite this, we can still benefit +// from having them in Go because it is much more cross-platform than bash scripts. +// +// These testlets are in their own repositories, and they do actually use some of the logic below, just not as meaningfully +// and comprehensively as they would have if they were baked in to the agent. Those testlets will still vendor this code +// and leverage the "Testlet" interface so that in the future, if we want to roll these into the todd-agent, those +// testlets will already conform to the standard provided below. + var ( - testletsMu sync.RWMutex - testlets = make(map[string]Testlet) - done = make(chan error) // Used from within the goroutine to inform the infrastructure it has finished - kill = make(chan bool) // Used from outside the goroutine to inform the goroutine to stop + testletsMu sync.RWMutex + testlets = make(map[string]Testlet) + done = make(chan error) // Used from within the goroutine to inform the infrastructure it has finished + kill = make(chan bool) // Used from outside the goroutine to inform the goroutine to stop + + // This map provides name redirection so that the native testlets can use names that don't + // conflict with existing system tools (i.e. using "toddping" instead of "ping") but users + // can still refer to the testlets using simple names. + // + // In short, users refer to the testlet by and this map will redirect to the + // actual binary name nativeTestlets = map[string]string{ "ping": "toddping", } @@ -73,7 +94,8 @@ func (b BaseTestlet) Run(target string, args []string, timeLimit int) (map[strin // kill = make(chan bool) // TODO(mierdin): Based on experimentation, this will keep running even if this function returns. - // Need to be sure about how this ends. Also might want to evaluate the same for the existing non-native model, likely has the same issue + // Need to be sure about how this ends. Also might want to evaluate the same for the existing + // non-native model, likely has the same issue go func() { theseMetrics, err := b.RunFunction(target, args, kill) metrics = theseMetrics //TODO(mierdin): Gross. diff --git a/scripts/buildtestlets.sh b/scripts/buildtestlets.sh deleted file mode 100755 index 0875fb1..0000000 --- a/scripts/buildtestlets.sh +++ /dev/null @@ -1,91 +0,0 @@ -#!/bin/bash - -# Copyright 2016 Matt Oswalt. Use or modification of this -# source code is governed by the license provided here: -# https://github.com/mierdin/todd/blob/master/LICENSE - -# This script downloads ToDD testlets prior to compile - -set -e -set -u -set -o pipefail - - -testlets=( - 'github.com/toddproject/todd-nativetestlet-ping' - ) - - -#rm -rf testlettemp && mkdir testlettemp && cd testlettemp - -for i in "${testlets[@]}" -do - #echo "Installing $i" - # git clone $i --quiet - go get -d -u $i/... -done - -# cd .. - -# rm -rf agent/testing/downloaded_testlets/ && mkdir agent/testing/downloaded_testlets - -# for dir in ./testlettemp/*/ -# do -# dir=${dir%*/} -# cp testlettemp/${dir##*/}/testlet/* agent/testing/downloaded_testlets -# #echo ${dir##*/} - - - -# testletdir="$(pwd)/$dir" -# #echo $testletdir - -# ln -s $testletdir/vendor/ $testletdir/vendor/src - -# # echo ./testlettemp/todd-nativetestlet-ping - -# # Append this vendor directory to GOPATH -# # TODO need to do some cleanup somewhere to remove this -# if [[ ":$GOPATH:" != *":$testletdir/vendor:"* ]]; then -# echo "export GOPATH=$GOPATH:$testletdir/vendor" -# fi - - -# done - - - - -# rm -rf testlettemp - - - -# rebuild plugins: -# _debug "removing: ${plugin_dir:?}/*" -# rm -rf "${plugin_dir:?}/"* -# mkdir -p "${plugin_dir}" - -# _info "building plugins" -# find "${__proj_dir}/plugin/" -type d -iname "snap-*" -print0 | xargs -0 -n 1 -I{} "${__dir}/build_plugin.sh" {} - -#--------- - - - -# __dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -# __proj_dir="$(dirname "$__dir")" - -# # shellcheck source=scripts/common.sh -# . "${__dir}/common.sh" - -# build_dir="${__proj_dir}/build" -# plugin_dir="${build_dir}/plugin" - -# plugin_src_path=$1 -# plugin_name=$(basename "${plugin_src_path}") -# go_build=(go build -a -ldflags "-w") - -# _debug "plugin source: ${plugin_src_path}" -# _info "building ${plugin_name}" - -# (cd "${plugin_src_path}" && "${go_build[@]}" -o "${plugin_dir}/${plugin_name}" . || exit 1) \ No newline at end of file diff --git a/scripts/gettestlets.sh b/scripts/gettestlets.sh new file mode 100755 index 0000000..c8e56ac --- /dev/null +++ b/scripts/gettestlets.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +# Copyright 2016 Matt Oswalt. Use or modification of this +# source code is governed by the license provided here: +# https://github.com/mierdin/todd/blob/master/LICENSE + +# This script installs ToDD-native testlets + +set -e +set -u +set -o pipefail + +# Install these testlets - comment out specific testlets to +# control what's installed +testlets=( + 'github.com/toddproject/todd-nativetestlet-ping' +) + +for i in "${testlets[@]}" +do + echo "Installing $i" + go get -u $i/cmd/... +done From 0e652b6984a26e0d5a2f1045b8745acb22a436ad Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Mon, 22 Aug 2016 23:43:58 -0700 Subject: [PATCH 015/120] Added note about future development efforts around testlets Signed-off-by: Matt Oswalt --- Makefile | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index f994d9a..d953dc9 100644 --- a/Makefile +++ b/Makefile @@ -11,8 +11,11 @@ build: docker build -t mierdin/todd -f Dockerfile . compile: - # TODO(mierdin): Need to support some kind of mode that allows for development (i.e. not downloading the testlets live, but rather from a directory?) - + # TODO(mierdin): Need to support some kind of mode that allows for development. + # The current gettestlets.sh script downloads the testlets from Github, meaning + # a developer would already have to have changes pushed to those repos' master + # Looking for something like devstack, etc. + # # Installing testlets ./scripts/gettestlets.sh From 7b0f02d15375c246e72b6aaf5ce24e13de4bee95 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Mon, 22 Aug 2016 23:51:29 -0700 Subject: [PATCH 016/120] Some fixes from reviewing the PR Signed-off-by: Matt Oswalt --- .gitignore | 1 - Vagrantfile | 2 +- agent/tasks/executetestrun.go | 21 ++++++++------------- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index 9896fa8..925635d 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,3 @@ preso_secret/ client/repeattest.sh *.out *.DS_Store* -agent/testing/downloaded_testlets/ \ No newline at end of file diff --git a/Vagrantfile b/Vagrantfile index a9efd37..9317dee 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -6,7 +6,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.box = "trusty64" config.vm.box_url = "http://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-amd64-vagrant-disk1.box" - config.vm.synced_folder '../../', '/home/vagrant/go/src/github.com', nfs: true + config.vm.synced_folder '.', '/home/vagrant/go/src/github.com/Mierdin/todd', nfs: true config.vm.provider "virtualbox" do |v| diff --git a/agent/tasks/executetestrun.go b/agent/tasks/executetestrun.go index de1ebbc..90776bb 100644 --- a/agent/tasks/executetestrun.go +++ b/agent/tasks/executetestrun.go @@ -25,16 +25,6 @@ import ( "github.com/Mierdin/todd/config" ) -var ( - // gatheredData represents test data from this agent for all targets. - // Key is target name, value is JSON output from testlet for that target - // This is reset to a blank map every time ExecuteTestRunTask is called - gatheredData = make(map[string]string) - - // Use a wait group to ensure that all of the testlets have a chance to finish - wg sync.WaitGroup -) - // ExecuteTestRunTask defines this particular task. type ExecuteTestRunTask struct { BaseTask @@ -48,8 +38,13 @@ type ExecuteTestRunTask struct { // a testrun will be executed once per target, all in parallel. func (ett ExecuteTestRunTask) Run() error { - // Make sure we're working with a clean slate - gatheredData = map[string]string{} + // gatheredData represents test data from this agent for all targets. + // Key is target name, value is JSON output from testlet for that target + // This is reset to a blank map every time ExecuteTestRunTask is called + gatheredData := map[string]string{} + + // Use a wait group to ensure that all of the testlets have a chance to finish + var wg sync.WaitGroup // Waiting three seconds to ensure all the agents have their tasks before we potentially hammer the network // TODO(mierdin): This is a temporary measure - in the future, testruns will be executed via time schedule, @@ -110,7 +105,7 @@ func (ett ExecuteTestRunTask) Run() error { // Execute collector cmd.Start() - // TODO(mierdin): Does this need to be a buffered channel? + // TODO(mierdin): Why is this a buffered channel? Is this necessary? done := make(chan error, 1) go func() { done <- cmd.Wait() From 687568392b20a8f541f1d4f46414dcb37cb0c6cb Mon Sep 17 00:00:00 2001 From: Kale Blankenship Date: Mon, 22 Aug 2016 23:04:51 -0800 Subject: [PATCH 017/120] Update IP field in DB and TSDB config section to Host (#78) --- etc/server-int.cfg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/etc/server-int.cfg b/etc/server-int.cfg index 3c809f9..040b69e 100644 --- a/etc/server-int.cfg +++ b/etc/server-int.cfg @@ -14,12 +14,12 @@ IP = 0.0.0.0 Port = 8090 [DB] -IP = etcd.todd-network +Host = etcd.todd-network Port = 4001 Plugin = etcd [TSDB] -IP = influx.todd-network +Host = influx.todd-network Port = 8086 Plugin = influxdb DatabaseName = todd_metrics From 9a42484345cc16069a265bac7bddf1c883a41c30 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Tue, 23 Aug 2016 00:31:25 -0700 Subject: [PATCH 018/120] Add comment for 'make start' and increase sleep timers for container init Signed-off-by: Matt Oswalt --- Makefile | 4 ++++ scripts/start-containers.sh | 9 ++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index a3e8efa..7e21cf1 100644 --- a/Makefile +++ b/Makefile @@ -30,6 +30,10 @@ update_assets: go-bindata -o assets/assets_unpack.go -pkg="assets" -prefix="agent" agent/testing/testlets/... agent/facts/collectors/... start: compile + + # This mode is just to get a demo of ToDD running within the VM quickly. + # It made sense to re-use the configurations for integration testing, so + # that's why "server-int.cfg" and "agent-int.cfg" are being used here. start-containers.sh 3 /etc/todd/server-int.cfg /etc/todd/agent-int.cfg configureenv: diff --git a/scripts/start-containers.sh b/scripts/start-containers.sh index 05fb8d2..92891b6 100755 --- a/scripts/start-containers.sh +++ b/scripts/start-containers.sh @@ -147,9 +147,10 @@ cleanup startinfra +# If first argument is "integration", start that topology and run tests if [ "$1" == "integration" ] then - sleep 5 + sleep 10 starttodd 6 /etc/todd/server-int.cfg /etc/todd/agent-int.cfg @@ -159,17 +160,19 @@ then exit $? fi +# If first argument is "interop", start that demo if [ "$1" == "interop" ] then - sleep 5 + sleep 10 starttodd 3 /etc/todd/server-interop.cfg /etc/todd/agent-interop.cfg exit $? fi +# Finally, if something's being passed in, it's probably number of agents, followed by configs. if [ -n "$1" ] then - sleep 5 + sleep 10 starttodd "$1" "$2" "$3" fi \ No newline at end of file From 3e10090596ecbfdbb12981c1ba88502b7537bc19 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Tue, 23 Aug 2016 22:43:25 -0700 Subject: [PATCH 019/120] Moved testlets outside to the testing package Signed-off-by: Matt Oswalt --- agent/testing/{testlets => }/testlets.go | 48 ++++++++++++------------ 1 file changed, 23 insertions(+), 25 deletions(-) rename agent/testing/{testlets => }/testlets.go (79%) diff --git a/agent/testing/testlets/testlets.go b/agent/testing/testlets.go similarity index 79% rename from agent/testing/testlets/testlets.go rename to agent/testing/testlets.go index ea35bce..dc4bb51 100644 --- a/agent/testing/testlets/testlets.go +++ b/agent/testing/testlets.go @@ -1,4 +1,12 @@ -package testlets +/* + ToDD task - set keyvalue pair in cache + + Copyright 2016 Matt Oswalt. Use or modification of this + source code is governed by the license provided here: + https://github.com/Mierdin/todd/blob/master/LICENSE +*/ + +package testing import ( "errors" @@ -17,13 +25,14 @@ import ( // running testlet code inside the agent could be controlled, and that new testlets could benefit from this // infrastructure. // -// Since then, the decision was made to keep testlets as their own separate binaries. Despite this, we can still benefit -// from having them in Go because it is much more cross-platform than bash scripts. +// Since then, the decision was made to keep testlets as their own separate binaries. // // These testlets are in their own repositories, and they do actually use some of the logic below, just not as meaningfully -// and comprehensively as they would have if they were baked in to the agent. Those testlets will still vendor this code -// and leverage the "Testlet" interface so that in the future, if we want to roll these into the todd-agent, those -// testlets will already conform to the standard provided below. +// and comprehensively as they would have if they were baked in to the agent. The development standard for all "blessed" +// testlets will still ensure that they use this interface, so that if we decide to bake them into the agent in the future, +// they'll already conform. +// +// (The vast majority of this code was inspired by the database drivers implementation in the stdlib) var ( testletsMu sync.RWMutex @@ -69,7 +78,6 @@ type Testlet interface { // without worrying about things like managing goroutines or channels. That's all // managed by the "Run" or "Kill" functions RunTestlet(string, []string, chan bool) (map[string]string, error) - // TODO(mierdin): is this really the best name for it? Maybe something that's less confusing, less like "Run" // All testlets must be able to stop operation when sent a Kill command. Kill() error @@ -89,16 +97,12 @@ func (b BaseTestlet) Run(target string, args []string, timeLimit int) (map[strin var metrics map[string]string - // TODO(mierdin): ensure channel is nil - // done = make(chan error) - // kill = make(chan bool) + // Ensure control channels are empty + done := make(chan error) + kill := make(chan bool) - // TODO(mierdin): Based on experimentation, this will keep running even if this function returns. - // Need to be sure about how this ends. Also might want to evaluate the same for the existing - // non-native model, likely has the same issue go func() { - theseMetrics, err := b.RunFunction(target, args, kill) - metrics = theseMetrics //TODO(mierdin): Gross. + metrics, err := b.RunFunction(target, args, kill) done <- err }() @@ -112,7 +116,7 @@ func (b BaseTestlet) Run(target string, args []string, timeLimit int) (map[strin case err := <-done: if err != nil { - return map[string]string{}, errors.New("testlet error") // TODO(mierdin): elaborate? + return map[string]string{}, errors.New("testlet error") } else { log.Debugf("Testlet completed without error") return metrics, nil @@ -120,13 +124,10 @@ func (b BaseTestlet) Run(target string, args []string, timeLimit int) (map[strin } } +// Kill is currently unimplemented. This will have to be coordinated with "Run". Basically +// you need a way to kill this testlet (and that's really only possible when running +// async) Probably just want to set the channel to something so the select within "Run" will execute func (b BaseTestlet) Kill() error { - // TODO (mierdin): This will have to be coordinated with the task above. Basically - // you need a way to kill this testlet (and that's really only possible when running - // async) - - // Probably just want to set the channel to something so the select within "Run" will execute - return nil } @@ -144,9 +145,6 @@ func IsNativeTestlet(name string) (bool, string) { func NewTestlet(name string) (Testlet, error) { if testlet, ok := testlets[name]; ok { - - // testlet.runFunction = testlet.run - return testlet, nil } else { return nil, errors.New( From f4041cca2ec2997abd6b2e3bb03a322e90800f60 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Sun, 18 Sep 2016 02:06:28 -0700 Subject: [PATCH 020/120] Updated testlets.go Signed-off-by: Matt Oswalt --- agent/testing/testlets/testlets.go | 51 ++++++++++++++++-------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/agent/testing/testlets/testlets.go b/agent/testing/testlets/testlets.go index ea35bce..004b0bd 100644 --- a/agent/testing/testlets/testlets.go +++ b/agent/testing/testlets/testlets.go @@ -1,3 +1,11 @@ +/* + ToDD task - set keyvalue pair in cache + + Copyright 2016 Matt Oswalt. Use or modification of this + source code is governed by the license provided here: + https://github.com/Mierdin/todd/blob/master/LICENSE +*/ + package testlets import ( @@ -17,13 +25,14 @@ import ( // running testlet code inside the agent could be controlled, and that new testlets could benefit from this // infrastructure. // -// Since then, the decision was made to keep testlets as their own separate binaries. Despite this, we can still benefit -// from having them in Go because it is much more cross-platform than bash scripts. +// Since then, the decision was made to keep testlets as their own separate binaries. // // These testlets are in their own repositories, and they do actually use some of the logic below, just not as meaningfully -// and comprehensively as they would have if they were baked in to the agent. Those testlets will still vendor this code -// and leverage the "Testlet" interface so that in the future, if we want to roll these into the todd-agent, those -// testlets will already conform to the standard provided below. +// and comprehensively as they would have if they were baked in to the agent. The development standard for all "blessed" +// testlets will still ensure that they use this interface, so that if we decide to bake them into the agent in the future, +// they'll already conform. +// +// (The vast majority of this code was inspired by the database drivers implementation in the stdlib) var ( testletsMu sync.RWMutex @@ -69,7 +78,6 @@ type Testlet interface { // without worrying about things like managing goroutines or channels. That's all // managed by the "Run" or "Kill" functions RunTestlet(string, []string, chan bool) (map[string]string, error) - // TODO(mierdin): is this really the best name for it? Maybe something that's less confusing, less like "Run" // All testlets must be able to stop operation when sent a Kill command. Kill() error @@ -89,16 +97,17 @@ func (b BaseTestlet) Run(target string, args []string, timeLimit int) (map[strin var metrics map[string]string - // TODO(mierdin): ensure channel is nil - // done = make(chan error) - // kill = make(chan bool) + // Ensure control channels are empty + done := make(chan error) + kill := make(chan bool) - // TODO(mierdin): Based on experimentation, this will keep running even if this function returns. - // Need to be sure about how this ends. Also might want to evaluate the same for the existing - // non-native model, likely has the same issue go func() { - theseMetrics, err := b.RunFunction(target, args, kill) - metrics = theseMetrics //TODO(mierdin): Gross. + metrics, err := b.RunFunction(target, args, kill) + + // TODO(mierdin): avoiding a "declared and not used" error for now + // If this code is ever actually used, it should be modified to make "done" a channel that returns the metrics, so it's actually used (just an idea) + log.Error(metrics) + done <- err }() @@ -112,7 +121,7 @@ func (b BaseTestlet) Run(target string, args []string, timeLimit int) (map[strin case err := <-done: if err != nil { - return map[string]string{}, errors.New("testlet error") // TODO(mierdin): elaborate? + return map[string]string{}, errors.New("testlet error") } else { log.Debugf("Testlet completed without error") return metrics, nil @@ -120,13 +129,10 @@ func (b BaseTestlet) Run(target string, args []string, timeLimit int) (map[strin } } +// Kill is currently unimplemented. This will have to be coordinated with "Run". Basically +// you need a way to kill this testlet (and that's really only possible when running +// async) Probably just want to set the channel to something so the select within "Run" will execute func (b BaseTestlet) Kill() error { - // TODO (mierdin): This will have to be coordinated with the task above. Basically - // you need a way to kill this testlet (and that's really only possible when running - // async) - - // Probably just want to set the channel to something so the select within "Run" will execute - return nil } @@ -144,9 +150,6 @@ func IsNativeTestlet(name string) (bool, string) { func NewTestlet(name string) (Testlet, error) { if testlet, ok := testlets[name]; ok { - - // testlet.runFunction = testlet.run - return testlet, nil } else { return nil, errors.New( From 588b23700227d46fb2248c56294404a377a36d91 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Mon, 19 Sep 2016 00:56:17 -0700 Subject: [PATCH 021/120] Various moves Signed-off-by: Matt Oswalt --- agent/testing/{reporting.go => testing.go} | 0 agent/testing/testlets.go | 201 --------------------- agent/testing/testlets/testlets.go | 56 ++++-- 3 files changed, 39 insertions(+), 218 deletions(-) rename agent/testing/{reporting.go => testing.go} (100%) delete mode 100644 agent/testing/testlets.go diff --git a/agent/testing/reporting.go b/agent/testing/testing.go similarity index 100% rename from agent/testing/reporting.go rename to agent/testing/testing.go diff --git a/agent/testing/testlets.go b/agent/testing/testlets.go deleted file mode 100644 index e93edcc..0000000 --- a/agent/testing/testlets.go +++ /dev/null @@ -1,201 +0,0 @@ -/* - ToDD task - set keyvalue pair in cache - - Copyright 2016 Matt Oswalt. Use or modification of this - source code is governed by the license provided here: - https://github.com/Mierdin/todd/blob/master/LICENSE -*/ - -<<<<<<< HEAD:agent/testing/testlets/testlets.go -package testlets -======= -package testing ->>>>>>> 3e10090596ecbfdbb12981c1ba88502b7537bc19:agent/testing/testlets.go - -import ( - "errors" - "fmt" - "sort" - "sync" - "time" - - log "github.com/Sirupsen/logrus" -) - -// NOTE -// -// Early efforts to build native-Go testlets involved the embedding of testlet logic into the -// ToDD agent itself. As a result, it was important to build some reusable infrastructure so that goroutines -// running testlet code inside the agent could be controlled, and that new testlets could benefit from this -// infrastructure. -// -// Since then, the decision was made to keep testlets as their own separate binaries. -// -// These testlets are in their own repositories, and they do actually use some of the logic below, just not as meaningfully -// and comprehensively as they would have if they were baked in to the agent. The development standard for all "blessed" -// testlets will still ensure that they use this interface, so that if we decide to bake them into the agent in the future, -// they'll already conform. -// -// (The vast majority of this code was inspired by the database drivers implementation in the stdlib) - -var ( - testletsMu sync.RWMutex - testlets = make(map[string]Testlet) - done = make(chan error) // Used from within the goroutine to inform the infrastructure it has finished - kill = make(chan bool) // Used from outside the goroutine to inform the goroutine to stop - - // This map provides name redirection so that the native testlets can use names that don't - // conflict with existing system tools (i.e. using "toddping" instead of "ping") but users - // can still refer to the testlets using simple names. - // - // In short, users refer to the testlet by and this map will redirect to the - // actual binary name - nativeTestlets = map[string]string{ - "ping": "toddping", - } -) - -// Testlet defines what a testlet should look like if built in native -// go and compiled with the agent -type Testlet interface { - - // Run is the "workflow" function for a testlet. It handles running - // the RunTestlet function asynchronously and managing the state therein. - // - // Params are - // target (string) - // args ([]string) - // timeLimit (int in seconds) - // - // Returns: - // metrics (map[string]interface{}) - // (name of metric is key, value is metric value) - // - // Keep as much logic out of here as possible. All native testlets - // must support a "Kill" method, so it's best to implement core testlet - // logic in a separate function so that the Run and Kill commands can manage - // execution of that logic in a goroutine - Run(string, []string, int) (map[string]string, error) - - // RunTestlet is designed to be the one-stop shop for testlet logic. - // The developer of a native testlet just needs to implement the testlet logic here, - // without worrying about things like managing goroutines or channels. That's all - // managed by the "Run" or "Kill" functions - RunTestlet(string, []string, chan bool) (map[string]string, error) - - // All testlets must be able to stop operation when sent a Kill command. - Kill() error -} - -type rtfunc func(target string, args []string, kill chan bool) (map[string]string, error) - -type BaseTestlet struct { - - // rtfunc is a type that will store our RunTestlet function. It is the responsibility - // of the "child" testlet to set this value upon creation - RunFunction rtfunc -} - -// Run takes care of running the testlet function and managing it's operation given the parameters provided -func (b BaseTestlet) Run(target string, args []string, timeLimit int) (map[string]string, error) { - - var metrics map[string]string - - // Ensure control channels are empty - done := make(chan error) - kill := make(chan bool) - - go func() { - metrics, err := b.RunFunction(target, args, kill) -<<<<<<< HEAD:agent/testing/testlets/testlets.go - - // TODO(mierdin): avoiding a "declared and not used" error for now - // If this code is ever actually used, it should be modified to make "done" a channel that returns the metrics, so it's actually used (just an idea) - log.Error(metrics) - -======= ->>>>>>> 3e10090596ecbfdbb12981c1ba88502b7537bc19:agent/testing/testlets.go - done <- err - }() - - // This select statement will block until one of these two conditions are met: - // - The testlet finishes, in which case the channel "done" will be receive a value - // - The configured time limit is exceeded (expected for testlets running in server mode) - select { - case <-time.After(time.Duration(timeLimit) * time.Second): - log.Debug("Successfully killed ") - return map[string]string{}, nil - - case err := <-done: - if err != nil { - return map[string]string{}, errors.New("testlet error") - } else { - log.Debugf("Testlet completed without error") - return metrics, nil - } - } -} - -// Kill is currently unimplemented. This will have to be coordinated with "Run". Basically -// you need a way to kill this testlet (and that's really only possible when running -// async) Probably just want to set the channel to something so the select within "Run" will execute -func (b BaseTestlet) Kill() error { - return nil -} - -// IsNativeTestlet polls the list of registered native testlets, and returns -// true if the referenced name exists -func IsNativeTestlet(name string) (bool, string) { - if _, ok := nativeTestlets[name]; ok { - return true, nativeTestlets[name] - } else { - return false, "" - } -} - -//NewTestlet produces a new testlet based on the "name" param -func NewTestlet(name string) (Testlet, error) { - - if testlet, ok := testlets[name]; ok { - return testlet, nil - } else { - return nil, errors.New( - fmt.Sprintf("'%s' not currently supported as a native testlet"), - ) - } -} - -// Register makes a testlet available by the provided name. -// If Register is called twice with the same name or if testlet is nil, -// it will return an error -func Register(name string, testlet Testlet) error { - testletsMu.Lock() - defer testletsMu.Unlock() - if testlet == nil { - return errors.New("Register testlet is nil") - } - if _, dup := testlets[name]; dup { - return errors.New("Register called twice for testlet " + name) - } - testlets[name] = testlet - return nil -} - -func unregisterAllTestlets() { - testletsMu.Lock() - defer testletsMu.Unlock() - // For tests. - testlets = make(map[string]Testlet) -} - -// Testlets returns a sorted list of the names of the registered testlets. -func Testlets() []string { - testletsMu.RLock() - defer testletsMu.RUnlock() - var list []string - for name := range testlets { - list = append(list, name) - } - sort.Strings(list) - return list -} diff --git a/agent/testing/testlets/testlets.go b/agent/testing/testlets/testlets.go index 004b0bd..fb6610d 100644 --- a/agent/testing/testlets/testlets.go +++ b/agent/testing/testlets/testlets.go @@ -18,22 +18,6 @@ import ( log "github.com/Sirupsen/logrus" ) -// NOTE -// -// Early efforts to build native-Go testlets involved the embedding of testlet logic into the -// ToDD agent itself. As a result, it was important to build some reusable infrastructure so that goroutines -// running testlet code inside the agent could be controlled, and that new testlets could benefit from this -// infrastructure. -// -// Since then, the decision was made to keep testlets as their own separate binaries. -// -// These testlets are in their own repositories, and they do actually use some of the logic below, just not as meaningfully -// and comprehensively as they would have if they were baked in to the agent. The development standard for all "blessed" -// testlets will still ensure that they use this interface, so that if we decide to bake them into the agent in the future, -// they'll already conform. -// -// (The vast majority of this code was inspired by the database drivers implementation in the stdlib) - var ( testletsMu sync.RWMutex testlets = make(map[string]Testlet) @@ -83,6 +67,22 @@ type Testlet interface { Kill() error } +// NOTE +// +// Early efforts to build native-Go testlets involved the embedding of testlet logic into the +// ToDD agent itself. As a result, it was important to build some reusable infrastructure so that goroutines +// running testlet code within the agent could be controlled, and that new testlets could benefit from this +// infrastructure. +// +// Since then, the decision was made to keep testlets as their own separate binaries. +// +// These testlets are in their own repositories, and they do actually use some of the logic below, just not as meaningfully +// and comprehensively as they would have if they were baked in to the agent. The development standard for all "blessed" +// testlets will still ensure that they use this interface, so that if we decide to bake them into the agent in the future, +// they'll already conform. +// +// (The vast majority of this code was inspired by the database drivers implementation in the stdlib) + type rtfunc func(target string, args []string, kill chan bool) (map[string]string, error) type BaseTestlet struct { @@ -105,7 +105,8 @@ func (b BaseTestlet) Run(target string, args []string, timeLimit int) (map[strin metrics, err := b.RunFunction(target, args, kill) // TODO(mierdin): avoiding a "declared and not used" error for now - // If this code is ever actually used, it should be modified to make "done" a channel that returns the metrics, so it's actually used (just an idea) + // If this code is ever actually used, it should be modified to make "done" + // a channel that returns the metrics, so it's actually used (just an idea) log.Error(metrics) done <- err @@ -161,6 +162,27 @@ func NewTestlet(name string) (Testlet, error) { // Register makes a testlet available by the provided name. // If Register is called twice with the same name or if testlet is nil, // it will return an error +// +// ======= +// EXAMPLE +// ======= +// // PingTestlet is one example that satisfies interface Testlet +// var pt = ping.PingTestlet{} + +// // Ensure the RunFunction attribute is set correctly. +// // This allows the underlying testlet infrastructure +// // to know what function to call at runtime +// pt.RunFunction = pt.RunTestlet + +// // This is important - register the name of this testlet +// // (the name the user will use in a testrun definition) +// //testlets.Register("ping", &pt) +// +// ======= +// +// TODO(mierdin): This is no longer used now that testlets are separate binaries, +// but one idea for the future could be that this function could register to the +//todd-agent via IPC of some kind, so that nativeTestlets is no longer needed func Register(name string, testlet Testlet) error { testletsMu.Lock() defer testletsMu.Unlock() From eec275fefe946f7db692f7483488e58dbf280c54 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Mon, 19 Sep 2016 22:49:40 -0700 Subject: [PATCH 022/120] removed all old work from testlets (moved to archive) Signed-off-by: Matt Oswalt --- agent/testing/testlets/testlets.go | 142 +---------------------------- 1 file changed, 5 insertions(+), 137 deletions(-) diff --git a/agent/testing/testlets/testlets.go b/agent/testing/testlets/testlets.go index fb6610d..8aa5443 100644 --- a/agent/testing/testlets/testlets.go +++ b/agent/testing/testlets/testlets.go @@ -9,13 +9,8 @@ package testlets import ( - "errors" - "fmt" - "sort" "sync" - "time" - - log "github.com/Sirupsen/logrus" + // log "github.com/Sirupsen/logrus" ) var ( @@ -39,8 +34,8 @@ var ( // go and compiled with the agent type Testlet interface { - // Run is the "workflow" function for a testlet. It handles running - // the RunTestlet function asynchronously and managing the state therein. + // Run is the "workflow" function for a testlet. All testing takes place here + // (or in a function called within) // // Params are // target (string) @@ -48,23 +43,9 @@ type Testlet interface { // timeLimit (int in seconds) // // Returns: - // metrics (map[string]interface{}) + // metrics (map[string]string) // (name of metric is key, value is metric value) - // - // Keep as much logic out of here as possible. All native testlets - // must support a "Kill" method, so it's best to implement core testlet - // logic in a separate function so that the Run and Kill commands can manage - // execution of that logic in a goroutine Run(string, []string, int) (map[string]string, error) - - // RunTestlet is designed to be the one-stop shop for testlet logic. - // The developer of a native testlet just needs to implement the testlet logic here, - // without worrying about things like managing goroutines or channels. That's all - // managed by the "Run" or "Kill" functions - RunTestlet(string, []string, chan bool) (map[string]string, error) - - // All testlets must be able to stop operation when sent a Kill command. - Kill() error } // NOTE @@ -83,7 +64,7 @@ type Testlet interface { // // (The vast majority of this code was inspired by the database drivers implementation in the stdlib) -type rtfunc func(target string, args []string, kill chan bool) (map[string]string, error) +type rtfunc func(target string, args []string, timeout int) (map[string]string, error) type BaseTestlet struct { @@ -92,51 +73,6 @@ type BaseTestlet struct { RunFunction rtfunc } -// Run takes care of running the testlet function and managing it's operation given the parameters provided -func (b BaseTestlet) Run(target string, args []string, timeLimit int) (map[string]string, error) { - - var metrics map[string]string - - // Ensure control channels are empty - done := make(chan error) - kill := make(chan bool) - - go func() { - metrics, err := b.RunFunction(target, args, kill) - - // TODO(mierdin): avoiding a "declared and not used" error for now - // If this code is ever actually used, it should be modified to make "done" - // a channel that returns the metrics, so it's actually used (just an idea) - log.Error(metrics) - - done <- err - }() - - // This select statement will block until one of these two conditions are met: - // - The testlet finishes, in which case the channel "done" will be receive a value - // - The configured time limit is exceeded (expected for testlets running in server mode) - select { - case <-time.After(time.Duration(timeLimit) * time.Second): - log.Debug("Successfully killed ") - return map[string]string{}, nil - - case err := <-done: - if err != nil { - return map[string]string{}, errors.New("testlet error") - } else { - log.Debugf("Testlet completed without error") - return metrics, nil - } - } -} - -// Kill is currently unimplemented. This will have to be coordinated with "Run". Basically -// you need a way to kill this testlet (and that's really only possible when running -// async) Probably just want to set the channel to something so the select within "Run" will execute -func (b BaseTestlet) Kill() error { - return nil -} - // IsNativeTestlet polls the list of registered native testlets, and returns // true if the referenced name exists func IsNativeTestlet(name string) (bool, string) { @@ -146,71 +82,3 @@ func IsNativeTestlet(name string) (bool, string) { return false, "" } } - -//NewTestlet produces a new testlet based on the "name" param -func NewTestlet(name string) (Testlet, error) { - - if testlet, ok := testlets[name]; ok { - return testlet, nil - } else { - return nil, errors.New( - fmt.Sprintf("'%s' not currently supported as a native testlet"), - ) - } -} - -// Register makes a testlet available by the provided name. -// If Register is called twice with the same name or if testlet is nil, -// it will return an error -// -// ======= -// EXAMPLE -// ======= -// // PingTestlet is one example that satisfies interface Testlet -// var pt = ping.PingTestlet{} - -// // Ensure the RunFunction attribute is set correctly. -// // This allows the underlying testlet infrastructure -// // to know what function to call at runtime -// pt.RunFunction = pt.RunTestlet - -// // This is important - register the name of this testlet -// // (the name the user will use in a testrun definition) -// //testlets.Register("ping", &pt) -// -// ======= -// -// TODO(mierdin): This is no longer used now that testlets are separate binaries, -// but one idea for the future could be that this function could register to the -//todd-agent via IPC of some kind, so that nativeTestlets is no longer needed -func Register(name string, testlet Testlet) error { - testletsMu.Lock() - defer testletsMu.Unlock() - if testlet == nil { - return errors.New("Register testlet is nil") - } - if _, dup := testlets[name]; dup { - return errors.New("Register called twice for testlet " + name) - } - testlets[name] = testlet - return nil -} - -func unregisterAllTestlets() { - testletsMu.Lock() - defer testletsMu.Unlock() - // For tests. - testlets = make(map[string]Testlet) -} - -// Testlets returns a sorted list of the names of the registered testlets. -func Testlets() []string { - testletsMu.RLock() - defer testletsMu.RUnlock() - var list []string - for name := range testlets { - list = append(list, name) - } - sort.Strings(list) - return list -} From f4734f1e77d332b8cd88a6d313e174d93f88280f Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Mon, 19 Sep 2016 23:03:05 -0700 Subject: [PATCH 023/120] Reorganized testing logic Signed-off-by: Matt Oswalt --- agent/tasks/executetestrun.go | 4 +- agent/tasks/installtestrun.go | 4 +- agent/testing/testing.go | 118 ++++++++++++++++------------- agent/testing/testlets/testlets.go | 84 -------------------- cmd/todd-agent/main.go | 54 +++++++++++-- 5 files changed, 116 insertions(+), 148 deletions(-) delete mode 100644 agent/testing/testlets/testlets.go diff --git a/agent/tasks/executetestrun.go b/agent/tasks/executetestrun.go index 90776bb..d3cf2be 100644 --- a/agent/tasks/executetestrun.go +++ b/agent/tasks/executetestrun.go @@ -21,7 +21,7 @@ import ( log "github.com/Sirupsen/logrus" "github.com/Mierdin/todd/agent/cache" - "github.com/Mierdin/todd/agent/testing/testlets" + "github.com/Mierdin/todd/agent/testing" "github.com/Mierdin/todd/config" ) @@ -66,7 +66,7 @@ func (ett ExecuteTestRunTask) Run() error { wg.Add(len(tr.Targets)) var testlet_path string - isNative, newTestletName := testlets.IsNativeTestlet(tr.Testlet) + isNative, newTestletName := testing.IsNativeTestlet(tr.Testlet) // If we're running a native testlet, we want testlet_path to simply be the testlet name // (since it is a requirement that the native-Go testlets are in the PATH) diff --git a/agent/tasks/installtestrun.go b/agent/tasks/installtestrun.go index 355e45e..ccfd711 100644 --- a/agent/tasks/installtestrun.go +++ b/agent/tasks/installtestrun.go @@ -20,7 +20,7 @@ import ( "github.com/Mierdin/todd/agent/cache" "github.com/Mierdin/todd/agent/defs" - "github.com/Mierdin/todd/agent/testing/testlets" + "github.com/Mierdin/todd/agent/testing" "github.com/Mierdin/todd/config" ) @@ -46,7 +46,7 @@ func (itt InstallTestRunTask) Run() error { var testlet_path string // Determine if this is a native testlet - isNative, newName := testlets.IsNativeTestlet(itt.Tr.Testlet) + isNative, newName := testing.IsNativeTestlet(itt.Tr.Testlet) // If we're running a native testlet, we want testlet_path to simply be the testlet name // (since it is a requirement that the native-Go testlets are in the PATH) diff --git a/agent/testing/testing.go b/agent/testing/testing.go index 58489ed..9155e9d 100644 --- a/agent/testing/testing.go +++ b/agent/testing/testing.go @@ -1,68 +1,78 @@ /* - ToDD Agent - Test Reporting + ToDD testing package - This file contains functions that watch the agent cache and upload test data when present. + Contains infrastructure running testlets as well as maintaining + conformance for other native-Go testlet projects - Copyright 2016 Matt Oswalt. Use or modification of this - source code is governed by the license provided here: - https://github.com/Mierdin/todd/blob/master/LICENSE + Copyright 2016 Matt Oswalt. Use or modification of this + source code is governed by the license provided here: + https://github.com/Mierdin/todd/blob/master/LICENSE */ package testing -import ( - "errors" - "time" +var ( - log "github.com/Sirupsen/logrus" - - "github.com/Mierdin/todd/agent/cache" - "github.com/Mierdin/todd/agent/responses" - "github.com/Mierdin/todd/comms" - "github.com/Mierdin/todd/config" + // This map provides name redirection so that the native testlets can use names that don't + // conflict with existing system tools (i.e. using "toddping" instead of "ping") but users + // can still refer to the testlets using simple names. + // + // In short, users refer to the testlet by and this map will redirect to the + // actual binary name + nativeTestlets = map[string]string{ + "ping": "toddping", + } ) -// WatchForFinishedTestRuns simply watches the local cache for any test runs that have test data. -// It will periodically look at the table and send any present test data back to the server as a response. -// When the server has successfully received this data, it will send a task back to this specific agent -// to delete this row from the cache. -func WatchForFinishedTestRuns(cfg config.Config) error { - - var ac = cache.NewAgentCache(cfg) - - agentUuid := ac.GetKeyValue("uuid") - - for { - - time.Sleep(5000 * time.Millisecond) - - testruns, err := ac.GetFinishedTestRuns() - if err != nil { - log.Error("Problem retrieving finished test runs") - return errors.New("Problem retrieving finished test runs") - } - - for testUuid, testData := range testruns { - - log.Debug("Found ripe testrun: ", testUuid) - - var utdr = responses.UploadTestDataResponse{ - TestUuid: testUuid, - TestData: testData, - } - utdr.AgentUuid = agentUuid - utdr.Type = "TestData" //TODO(mierdin): This is an extra step. Maybe a factory function for the task could help here? - - tc, err := comms.NewToDDComms(cfg) - if err != nil { - return err - } - tc.CommsPackage.SendResponse(utdr) +// Testlet defines what a testlet should look like if built in native +// go and compiled with the agent +type Testlet interface { + + // Run is the "workflow" function for a testlet. All testing takes place here + // (or in a function called within) + // + // Params are + // target (string) + // args ([]string) + // timeLimit (int in seconds) + // + // Returns: + // metrics (map[string]string) + // (name of metric is key, value is metric value) + Run(string, []string, int) (map[string]string, error) +} - } +// NOTE +// +// Early efforts to build native-Go testlets involved the embedding of testlet logic into the +// ToDD agent itself. As a result, it was important to build some reusable infrastructure so that goroutines +// running testlet code within the agent could be controlled, and that new testlets could benefit from this +// infrastructure. +// +// Since then, the decision was made to keep testlets as their own separate binaries. +// +// These testlets are in their own repositories, and they do actually use some of the logic below, just not as meaningfully +// and comprehensively as they would have if they were baked in to the agent. The development standard for all "blessed" +// testlets will still ensure that they use this interface, so that if we decide to bake them into the agent in the future, +// they'll already conform. +// +// (The vast majority of this code was inspired by the database drivers implementation in the stdlib) + +type rtfunc func(target string, args []string, timeout int) (map[string]string, error) + +type BaseTestlet struct { + + // rtfunc is a type that will store our RunTestlet function. It is the responsibility + // of the "child" testlet to set this value upon creation + RunFunction rtfunc +} +// IsNativeTestlet polls the list of registered native testlets, and returns +// true if the referenced name exists +func IsNativeTestlet(name string) (bool, string) { + if _, ok := nativeTestlets[name]; ok { + return true, nativeTestlets[name] + } else { + return false, "" } - - return nil - } diff --git a/agent/testing/testlets/testlets.go b/agent/testing/testlets/testlets.go deleted file mode 100644 index 8aa5443..0000000 --- a/agent/testing/testlets/testlets.go +++ /dev/null @@ -1,84 +0,0 @@ -/* - ToDD task - set keyvalue pair in cache - - Copyright 2016 Matt Oswalt. Use or modification of this - source code is governed by the license provided here: - https://github.com/Mierdin/todd/blob/master/LICENSE -*/ - -package testlets - -import ( - "sync" - // log "github.com/Sirupsen/logrus" -) - -var ( - testletsMu sync.RWMutex - testlets = make(map[string]Testlet) - done = make(chan error) // Used from within the goroutine to inform the infrastructure it has finished - kill = make(chan bool) // Used from outside the goroutine to inform the goroutine to stop - - // This map provides name redirection so that the native testlets can use names that don't - // conflict with existing system tools (i.e. using "toddping" instead of "ping") but users - // can still refer to the testlets using simple names. - // - // In short, users refer to the testlet by and this map will redirect to the - // actual binary name - nativeTestlets = map[string]string{ - "ping": "toddping", - } -) - -// Testlet defines what a testlet should look like if built in native -// go and compiled with the agent -type Testlet interface { - - // Run is the "workflow" function for a testlet. All testing takes place here - // (or in a function called within) - // - // Params are - // target (string) - // args ([]string) - // timeLimit (int in seconds) - // - // Returns: - // metrics (map[string]string) - // (name of metric is key, value is metric value) - Run(string, []string, int) (map[string]string, error) -} - -// NOTE -// -// Early efforts to build native-Go testlets involved the embedding of testlet logic into the -// ToDD agent itself. As a result, it was important to build some reusable infrastructure so that goroutines -// running testlet code within the agent could be controlled, and that new testlets could benefit from this -// infrastructure. -// -// Since then, the decision was made to keep testlets as their own separate binaries. -// -// These testlets are in their own repositories, and they do actually use some of the logic below, just not as meaningfully -// and comprehensively as they would have if they were baked in to the agent. The development standard for all "blessed" -// testlets will still ensure that they use this interface, so that if we decide to bake them into the agent in the future, -// they'll already conform. -// -// (The vast majority of this code was inspired by the database drivers implementation in the stdlib) - -type rtfunc func(target string, args []string, timeout int) (map[string]string, error) - -type BaseTestlet struct { - - // rtfunc is a type that will store our RunTestlet function. It is the responsibility - // of the "child" testlet to set this value upon creation - RunFunction rtfunc -} - -// IsNativeTestlet polls the list of registered native testlets, and returns -// true if the referenced name exists -func IsNativeTestlet(name string) (bool, string) { - if _, ok := nativeTestlets[name]; ok { - return true, nativeTestlets[name] - } else { - return false, "" - } -} diff --git a/cmd/todd-agent/main.go b/cmd/todd-agent/main.go index a7f527e..aa39c81 100644 --- a/cmd/todd-agent/main.go +++ b/cmd/todd-agent/main.go @@ -9,6 +9,7 @@ package main import ( + "errors" "flag" "fmt" "os" @@ -19,14 +20,10 @@ import ( "github.com/Mierdin/todd/agent/cache" "github.com/Mierdin/todd/agent/defs" "github.com/Mierdin/todd/agent/facts" - "github.com/Mierdin/todd/agent/testing" + "github.com/Mierdin/todd/agent/responses" "github.com/Mierdin/todd/comms" "github.com/Mierdin/todd/config" "github.com/Mierdin/todd/hostresources" - // Testlet imports (importing these packages registers the testlets) - // Need to make this dynamic if possible (at compile time of course) - // TODO Not necessary anymore - //_ "github.com/toddproject/todd-nativetestlet-ping/testlet" ) // Command-line Arguments @@ -70,7 +67,7 @@ func main() { log.Infof("ToDD Agent Activated: %s", uuid) // Start test data reporting service - go testing.WatchForFinishedTestRuns(cfg) + go watchForFinishedTestRuns(cfg) // Construct comms package tc, err := comms.NewToDDComms(cfg) @@ -124,3 +121,48 @@ func main() { } } + +// watchForFinishedTestRuns simply watches the local cache for any test runs that have test data. +// It will periodically look at the table and send any present test data back to the server as a response. +// When the server has successfully received this data, it will send a task back to this specific agent +// to delete this row from the cache. +func watchForFinishedTestRuns(cfg config.Config) error { + + var ac = cache.NewAgentCache(cfg) + + agentUuid := ac.GetKeyValue("uuid") + + for { + + time.Sleep(5000 * time.Millisecond) + + testruns, err := ac.GetFinishedTestRuns() + if err != nil { + log.Error("Problem retrieving finished test runs") + return errors.New("Problem retrieving finished test runs") + } + + for testUuid, testData := range testruns { + + log.Debug("Found ripe testrun: ", testUuid) + + var utdr = responses.UploadTestDataResponse{ + TestUuid: testUuid, + TestData: testData, + } + utdr.AgentUuid = agentUuid + utdr.Type = "TestData" //TODO(mierdin): This is an extra step. Maybe a factory function for the task could help here? + + tc, err := comms.NewToDDComms(cfg) + if err != nil { + return err + } + tc.CommsPackage.SendResponse(utdr) + + } + + } + + return nil + +} From 7cae0cf701c0c5ef4f7f2af6641e6c18179f53c5 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Tue, 20 Sep 2016 14:51:01 -0700 Subject: [PATCH 024/120] updated comment regarding future development script Signed-off-by: Matt Oswalt --- Makefile | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index d953dc9..68377eb 100644 --- a/Makefile +++ b/Makefile @@ -11,11 +11,13 @@ build: docker build -t mierdin/todd -f Dockerfile . compile: - # TODO(mierdin): Need to support some kind of mode that allows for development. - # The current gettestlets.sh script downloads the testlets from Github, meaning - # a developer would already have to have changes pushed to those repos' master - # Looking for something like devstack, etc. + # TODO(mierdin): The current gettestlets.sh script downloads the testlets from + # Github, meaning a developer would already have to have changes pushed to + # those repos' master. # + # In a follow-up patch, a script will be provided that allows for rapid development + # (uses local repositories instead of pulling from GH). Something like devstack. + # Installing testlets ./scripts/gettestlets.sh From 880e07b0294b02cd80944177e3ee137498c0384f Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Tue, 20 Sep 2016 15:06:44 -0700 Subject: [PATCH 025/120] removed sudo from sysctl command in makefile Signed-off-by: Matt Oswalt --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 68377eb..a0581e5 100644 --- a/Makefile +++ b/Makefile @@ -50,4 +50,4 @@ configureenv: chmod -R 777 /opt/todd # If on Linux, enable ping testlet functionality - sudo sysctl -w net.ipv4.ping_group_range="0 12345" + sysctl -w net.ipv4.ping_group_range="0 12345" From 8c4b98140881e44cd2e6ba29bb69c35da4a8d4d2 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Tue, 20 Sep 2016 17:05:26 -0700 Subject: [PATCH 026/120] Updates to makefile and vagrantfile Signed-off-by: Matt Oswalt --- Makefile | 2 +- Vagrantfile | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a0581e5..beb8953 100644 --- a/Makefile +++ b/Makefile @@ -50,4 +50,4 @@ configureenv: chmod -R 777 /opt/todd # If on Linux, enable ping testlet functionality - sysctl -w net.ipv4.ping_group_range="0 12345" + sysctl -w net.ipv4.ping_group_range="0 0" || echo "Unable to set kernel parameters to allow ping. Some testlets may not work." diff --git a/Vagrantfile b/Vagrantfile index 9317dee..dc26d0e 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -7,6 +7,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.box = "trusty64" config.vm.box_url = "http://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-amd64-vagrant-disk1.box" config.vm.synced_folder '.', '/home/vagrant/go/src/github.com/Mierdin/todd', nfs: true + config.vm.synced_folder '../../toddproject/todd-nativetestlet-ping', '/home/vagrant/go/src/github.com/toddproject/todd-nativetestlet-ping', nfs: true config.vm.provider "virtualbox" do |v| From 5f1c30b359c7cfc66471680579d6ed33bf6e6c9a Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Thu, 22 Sep 2016 14:10:43 -0700 Subject: [PATCH 027/120] Added script to add socket capabilities to testlet binaries on install Signed-off-by: Matt Oswalt --- Makefile | 8 +++++--- scripts/set-testlet-capabilities.sh | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 scripts/set-testlet-capabilities.sh diff --git a/Makefile b/Makefile index beb8953..566ab8c 100644 --- a/Makefile +++ b/Makefile @@ -24,8 +24,6 @@ compile: # Installing ToDD go install ./cmd/... -install: configureenv - fmt: go fmt github.com/mierdin/todd/... @@ -43,7 +41,11 @@ update_assets: start: compile start-containers.sh 3 /etc/todd/server-int.cfg /etc/todd/agent-int.cfg -configureenv: +install: + + # Set testlet capabilities + ./scripts/set-testlet-capabilities.sh + # Copy configs if etc and /etc/todd aren't linked if ! [ "etc" -ef "/etc/todd" ]; then mkdir -p /etc/todd && cp -f ./etc/{agent,server}.cfg /etc/todd/; fi mkdir -p /opt/todd/{agent,server}/assets/{factcollectors,testlets} diff --git a/scripts/set-testlet-capabilities.sh b/scripts/set-testlet-capabilities.sh new file mode 100644 index 0000000..f37a749 --- /dev/null +++ b/scripts/set-testlet-capabilities.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +# Copyright 2016 Matt Oswalt. Use or modification of this +# source code is governed by the license provided here: +# https://github.com/mierdin/todd/blob/master/LICENSE + +# This script configures appropriate capabilities for testlet binaries + +set -e +set -u +set -o pipefail + +# Ensure GOPATH is set +: ${GOPATH:?"Please ensure GOPATH is set, and run sudo with -E when performing 'make install'"} + +# Enable raw socket capabilities on toddping +setcap cap_net_raw+ep $GOPATH/bin/toddping \ No newline at end of file From 14d99291c7f691fa2bec355860f4f69a578e7ad8 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Thu, 22 Sep 2016 14:12:59 -0700 Subject: [PATCH 028/120] Made capabilities script exec Signed-off-by: Matt Oswalt --- scripts/set-testlet-capabilities.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 scripts/set-testlet-capabilities.sh diff --git a/scripts/set-testlet-capabilities.sh b/scripts/set-testlet-capabilities.sh old mode 100644 new mode 100755 From 74083159887b48579f56a0f872bb36b56edc0b39 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Thu, 22 Sep 2016 15:09:12 -0700 Subject: [PATCH 029/120] Added some comments about install process Signed-off-by: Matt Oswalt --- Makefile | 10 ++-------- scripts/gettestlets.sh | 2 ++ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 566ab8c..714a1be 100644 --- a/Makefile +++ b/Makefile @@ -11,12 +11,6 @@ build: docker build -t mierdin/todd -f Dockerfile . compile: - # TODO(mierdin): The current gettestlets.sh script downloads the testlets from - # Github, meaning a developer would already have to have changes pushed to - # those repos' master. - # - # In a follow-up patch, a script will be provided that allows for rapid development - # (uses local repositories instead of pulling from GH). Something like devstack. # Installing testlets ./scripts/gettestlets.sh @@ -51,5 +45,5 @@ install: mkdir -p /opt/todd/{agent,server}/assets/{factcollectors,testlets} chmod -R 777 /opt/todd - # If on Linux, enable ping testlet functionality - sysctl -w net.ipv4.ping_group_range="0 0" || echo "Unable to set kernel parameters to allow ping. Some testlets may not work." + # If on Linux, enable ping testlet functionality (DEPRECATED in favor of granting socket capabilities on testlets) + # sysctl -w net.ipv4.ping_group_range="0 0" || echo "Unable to set kernel parameters to allow ping. Some testlets may not work." diff --git a/scripts/gettestlets.sh b/scripts/gettestlets.sh index c8e56ac..e34a431 100755 --- a/scripts/gettestlets.sh +++ b/scripts/gettestlets.sh @@ -19,5 +19,7 @@ testlets=( for i in "${testlets[@]}" do echo "Installing $i" + + # This retrieves from GH if it doesn't exist, but if it does, it installs the local copy. go get -u $i/cmd/... done From 404525794f0d9cd56a2ed3664d81a25a0258f15f Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Tue, 19 Jul 2016 01:35:36 -0700 Subject: [PATCH 030/120] Saving progress on building native testlet infrastructure Signed-off-by: Matt Oswalt --- agent/tasks/executetestrun.go | 117 +++++++++++++++---------- agent/tasks/installtestrun.go | 56 +++++++----- agent/testing/testlets/{ping => _ping} | 0 agent/testing/testlets/ping/ping.go | 46 ++++++++++ agent/testing/testlets/testlets.go | 74 +++++++++++++++- cmd/todd-agent/main.go | 6 +- 6 files changed, 229 insertions(+), 70 deletions(-) rename agent/testing/testlets/{ping => _ping} (100%) create mode 100644 agent/testing/testlets/ping/ping.go diff --git a/agent/tasks/executetestrun.go b/agent/tasks/executetestrun.go index 6ed6e48..09904b0 100644 --- a/agent/tasks/executetestrun.go +++ b/agent/tasks/executetestrun.go @@ -21,9 +21,20 @@ import ( log "github.com/Sirupsen/logrus" "github.com/Mierdin/todd/agent/cache" + "github.com/Mierdin/todd/agent/testing/testlets" "github.com/Mierdin/todd/config" ) +var ( + // gatheredData represents test data from this agent for all targets. + // Key is target name, value is JSON output from testlet for that target + // This is reset to a blank map every time ExecuteTestRunTask is called + gatheredData = make(map[string]string) + + // Use a wait group to ensure that all of the testlets have a chance to finish + wg sync.WaitGroup +) + // ExecuteTestRunTask defines this particular task. type ExecuteTestRunTask struct { BaseTask @@ -37,6 +48,9 @@ type ExecuteTestRunTask struct { // a testrun will be executed once per target, all in parallel. func (ett ExecuteTestRunTask) Run() error { + // Make sure we're working with a clean slate + gatheredData = map[string]string{} + // Waiting three seconds to ensure all the agents have their tasks before we potentially hammer the network // TODO(mierdin): This is a bit of a copout. I would like to do something a little more robust than simply waiting // for a few seconds in the future. @@ -50,70 +64,79 @@ func (ett ExecuteTestRunTask) Run() error { return errors.New("Problem retrieving testrun from agent cache") } - // Generate path to testlet and make sure it exists. - testlet_path := fmt.Sprintf("%s/assets/testlets/%s", ett.Config.LocalResources.OptDir, tr.Testlet) - if _, err := os.Stat(testlet_path); os.IsNotExist(err) { - log.Errorf("Testlet %s does not exist on this agent", tr.Testlet) - return errors.New("Error executing testrun - testlet doesn't exist on this agent.") - } - log.Debugf("IMMA FIRIN MAH LAZER (for test %s) ", ett.TestUuid) - // Use a wait group to ensure that all of the testlets have a chance to finish - var wg sync.WaitGroup + // Specify size of wait group wg.Add(len(tr.Targets)) - // gatheredData represents test data from this agent for all targets. - // Key is target name, value is JSON output from testlet for that target - gatheredData := make(map[string]string) + // Determine if this is a native testlet + nativeTestlet, err := testlets.NewTestlet(tr.Testlet) + native := (err == nil) // Execute testlets against all targets asynchronously for i := range tr.Targets { thisTarget := tr.Targets[i] - go func() { - defer wg.Done() - - log.Debugf("Full testlet command and args: '%s %s %s'", testlet_path, thisTarget, tr.Args) - cmd := exec.Command(testlet_path, thisTarget, tr.Args) - - // Stdout buffer - cmdOutput := &bytes.Buffer{} - // Attach buffer to command - cmd.Stdout = cmdOutput + if native { + go func() { - // Execute collector - cmd.Start() + // TODO(mierdin): Just calling a temporary method to ensure we can get to the native testlet. + // (it works!) + log.Error(nativeTestlet.Test()) + }() + } else { + // Generate path to testlet and make sure it exists. + testlet_path := fmt.Sprintf("%s/assets/testlets/%s", ett.Config.LocalResources.OptDir, tr.Testlet) + if _, err := os.Stat(testlet_path); os.IsNotExist(err) { + log.Errorf("Testlet %s does not exist on this agent", tr.Testlet) + return errors.New("Error executing testrun - testlet doesn't exist on this agent.") + } - done := make(chan error, 1) go func() { - done <- cmd.Wait() - }() - // This select statement will block until one of these two conditions are met: - // - The testlet finishes, in which case the channel "done" will be receive a value - // - The configured time limit is exceeded (expected for testlets running in server mode) - select { - case <-time.After(time.Duration(ett.TimeLimit) * time.Second): - if err := cmd.Process.Kill(); err != nil { - log.Errorf("Failed to kill %s after timeout: %s", testlet_path, err) - } else { - log.Debug("Successfully killed ", testlet_path) - } - case err := <-done: - if err != nil { - log.Errorf("Testlet %s completed with error '%s'", testlet_path, err) - gatheredData[thisTarget] = "error" - } else { - log.Debugf("Testlet %s completed without error", testlet_path) + defer wg.Done() + + log.Debugf("Full testlet command and args: '%s %s %s'", testlet_path, thisTarget, tr.Args) + cmd := exec.Command(testlet_path, thisTarget, tr.Args) + + // Stdout buffer + cmdOutput := &bytes.Buffer{} + // Attach buffer to command + cmd.Stdout = cmdOutput + + // Execute collector + cmd.Start() + + done := make(chan error, 1) + go func() { + done <- cmd.Wait() + }() + + // This select statement will block until one of these two conditions are met: + // - The testlet finishes, in which case the channel "done" will be receive a value + // - The configured time limit is exceeded (expected for testlets running in server mode) + select { + case <-time.After(time.Duration(ett.TimeLimit) * time.Second): + if err := cmd.Process.Kill(); err != nil { + log.Errorf("Failed to kill %s after timeout: %s", testlet_path, err) + } else { + log.Debug("Successfully killed ", testlet_path) + } + case err := <-done: + if err != nil { + log.Errorf("Testlet %s completed with error '%s'", testlet_path, err) + gatheredData[thisTarget] = "error" + } else { + log.Debugf("Testlet %s completed without error", testlet_path) + } } - } - // Record test data - gatheredData[thisTarget] = string(cmdOutput.Bytes()) + // Record test data + gatheredData[thisTarget] = string(cmdOutput.Bytes()) - }() + }() + } } wg.Wait() diff --git a/agent/tasks/installtestrun.go b/agent/tasks/installtestrun.go index c45da42..131df4d 100644 --- a/agent/tasks/installtestrun.go +++ b/agent/tasks/installtestrun.go @@ -20,6 +20,7 @@ import ( "github.com/Mierdin/todd/agent/cache" "github.com/Mierdin/todd/agent/defs" + "github.com/Mierdin/todd/agent/testing/testlets" "github.com/Mierdin/todd/config" ) @@ -42,35 +43,48 @@ func (itt InstallTestRunTask) Run() error { return errors.New("Testlet parameter for this testrun is null") } - // Generate path to testlet and make sure it exists. - testlet_path := fmt.Sprintf("%s/assets/testlets/%s", itt.Config.LocalResources.OptDir, itt.Tr.Testlet) - if _, err := os.Stat(testlet_path); os.IsNotExist(err) { - log.Errorf("Testlet %s does not exist on this agent", itt.Tr.Testlet) - return errors.New("Error installing testrun - testlet doesn't exist on this agent.") - } + // Determine if this is a valid native testlet + _, err := testlets.NewTestlet(itt.Tr.Testlet) + + // Not a native testlet - attempt to run check mode on testlet in filesystem + if err != nil { + + // Generate path to testlet and make sure it exists. + testlet_path := fmt.Sprintf("%s/assets/testlets/%s", itt.Config.LocalResources.OptDir, itt.Tr.Testlet) + if _, err := os.Stat(testlet_path); os.IsNotExist(err) { + log.Errorf("Testlet %s does not exist on this agent", itt.Tr.Testlet) + return errors.New("Error installing testrun - testlet doesn't exist on this agent.") + } - // Run the testlet in check mode to verify that everything is okay to run this test - log.Debug("Running testlet in check mode: ", testlet_path) - cmd := exec.Command(testlet_path, "check") + // Run the testlet in check mode to verify that everything is okay to run this test + log.Debug("Running testlet in check mode: ", testlet_path) + cmd := exec.Command(testlet_path, "check") - // Stdout buffer - cmdOutput := &bytes.Buffer{} - // Attach buffer to command - cmd.Stdout = cmdOutput - // Execute collector - cmd.Run() + // Stdout buffer + cmdOutput := &bytes.Buffer{} + // Attach buffer to command + cmd.Stdout = cmdOutput + // Execute collector + cmd.Run() + + // This is probably the best cross-platform way to see if check mode passed. + if strings.Contains(string(cmdOutput.Bytes()), "Check mode PASSED") { + log.Debugf("Check mode for %s passed", testlet_path) + } else { + log.Error("Testlet returned an error during check mode: ", string(cmdOutput.Bytes())) + return errors.New("Testlet returned an error during check mode") + } - // This is probably the best cross-platform way to see if check mode passed. - if strings.Contains(string(cmdOutput.Bytes()), "Check mode PASSED") { - log.Debugf("Check mode for %s passed", testlet_path) } else { - log.Error("Testlet returned an error during check mode: ", string(cmdOutput.Bytes())) - return errors.New("Testlet returned an error during check mode") + + // Nothing to do, as we're using a native testlet + log.Infof("%s is a native testlet - installing testrun.", itt.Tr.Testlet) + } // Insert testrun into agent cache var ac = cache.NewAgentCache(itt.Config) - err := ac.InsertTestRun(itt.Tr) + err = ac.InsertTestRun(itt.Tr) if err != nil { log.Error(err) return errors.New("Problem installing test run into agent cache") diff --git a/agent/testing/testlets/ping b/agent/testing/testlets/_ping similarity index 100% rename from agent/testing/testlets/ping rename to agent/testing/testlets/_ping diff --git a/agent/testing/testlets/ping/ping.go b/agent/testing/testlets/ping/ping.go new file mode 100644 index 0000000..f68b43b --- /dev/null +++ b/agent/testing/testlets/ping/ping.go @@ -0,0 +1,46 @@ +package toddping + +// NOTE ////////////////// +// +// This is a built-in testlet. Currently, the approach is to have each +// testlet under it's own package, which is explicitly imported under +// the ToDD agent's 'main' package. +// +// Currently, each testlet is stored within the ToDD repo in order to +// vet out the architecture, which means they are awkwardly placed +// underneath the "testlets" directory together. However, this should be +// a tempoarary holding place, as the main effort around native testlets +// is being implemented so they can be broken out into their own repos. + +import ( + "github.com/Mierdin/todd/agent/testing/testlets" +) + +type PingTestlet struct{} + +func init() { + testlets.Register("ping", &PingTestlet{}) +} + +// TODO(mierdin): Maybe consider running these asyc by default? Basically +// the "Run" function kicks back a channel of type map[string]string so when +// it's populated, it contains the metrics and you know it can stop + +func (p PingTestlet) Run(target string, args []string) (map[string]string, error) { + + // TODO(mierdin): Implement ping test here + + return map[string]string{}, nil +} + +func (p PingTestlet) Kill() error { + // TODO (mierdin): This will have to be coordinated with the task above. Basically + // you need a way to kill this testlet (and that's really only possible when running + // async) + + return nil +} + +func (p PingTestlet) Test() string { + return "trolololol" +} diff --git a/agent/testing/testlets/testlets.go b/agent/testing/testlets/testlets.go index fc84175..6ad65cf 100644 --- a/agent/testing/testlets/testlets.go +++ b/agent/testing/testlets/testlets.go @@ -1,7 +1,79 @@ package testlets +import ( + "errors" + "fmt" + "sort" + "sync" + //"sync/atomic" +) + +var ( + testletsMu sync.RWMutex + testlets = make(map[string]Testlet) +) + // Testlet defines what a testlet should look like if built in native // go and compiled with the agent type Testlet interface { - Run(string, []string) ([]byte, error) + + // Params are + // target (string) + // args ([]string) + // + // Returns: + // metrics (map[string]interface{}) + // (name of metric is key, value is metric value) + Run(string, []string) (map[string]string, error) + + Kill() error + + Test() string //TODO(mierdin): Remove me +} + +//NewTestlet produces a new testlet based on the "name" param +func NewTestlet(name string) (Testlet, error) { + + if testlet, ok := testlets[name]; ok { + return testlet, nil + } else { + return nil, errors.New( + fmt.Sprintf("'%s' not currently supported as a native testlet"), + ) + } +} + +// Register makes a testlet available by the provided name. +// If Register is called twice with the same name or if testlet is nil, +// it will return an error +func Register(name string, testlet Testlet) error { + testletsMu.Lock() + defer testletsMu.Unlock() + if testlet == nil { + return errors.New("Register testlet is nil") + } + if _, dup := testlets[name]; dup { + return errors.New("Register called twice for testlet " + name) + } + testlets[name] = testlet + return nil +} + +func unregisterAllDrivers() { + testletsMu.Lock() + defer testletsMu.Unlock() + // For tests. + testlets = make(map[string]Testlet) +} + +// Testlets returns a sorted list of the names of the registered testlets. +func Testlets() []string { + testletsMu.RLock() + defer testletsMu.RUnlock() + var list []string + for name := range testlets { + list = append(list, name) + } + sort.Strings(list) + return list } diff --git a/cmd/todd-agent/main.go b/cmd/todd-agent/main.go index 51f75ad..30950d9 100644 --- a/cmd/todd-agent/main.go +++ b/cmd/todd-agent/main.go @@ -14,6 +14,8 @@ import ( "os" "time" + log "github.com/Sirupsen/logrus" + "github.com/Mierdin/todd/agent/cache" "github.com/Mierdin/todd/agent/defs" "github.com/Mierdin/todd/agent/facts" @@ -21,7 +23,9 @@ import ( "github.com/Mierdin/todd/comms" "github.com/Mierdin/todd/config" "github.com/Mierdin/todd/hostresources" - log "github.com/Sirupsen/logrus" + + // Testlet imports (importing these packages registers the testlets) + _ "github.com/Mierdin/todd/agent/testing/testlets/ping" ) // Command-line Arguments From 967ed0df4ba9dc073e8cf324710606dc5626aa95 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Tue, 19 Jul 2016 01:36:05 -0700 Subject: [PATCH 031/120] Docs update Signed-off-by: Matt Oswalt --- docs/nativetests.rst | 6 ++++++ docs/testlets.rst | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 docs/nativetests.rst diff --git a/docs/nativetests.rst b/docs/nativetests.rst new file mode 100644 index 0000000..e6c04a3 --- /dev/null +++ b/docs/nativetests.rst @@ -0,0 +1,6 @@ +Native Tests +================================ + +Need to talk about the native tests you've built in, and turn the "testlets" doc into more of a "so you want to build your own, eh?" + +Also need to figure out if you want to refer to both native and non-native as "testlets", or maybe reserve that for non-native \ No newline at end of file diff --git a/docs/testlets.rst b/docs/testlets.rst index fab1508..06afe4d 100644 --- a/docs/testlets.rst +++ b/docs/testlets.rst @@ -18,7 +18,7 @@ When you want to run a certain testlet, you refer to it by name. There are a num * bandwidth * ping -You can, of course, build your own testlet (provided it follows the standard defined on this page) and refer to it by it's filename. +If you don't want to use any of the built-in testlets, you can, of course, build your own testlet (provided it follows the standard defined on this page) and refer to it by it's filename. Check Mode ---------- From b2acdddc7419b52529f49c120b9f4e1a30138feb Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Thu, 28 Jul 2016 01:45:12 -0700 Subject: [PATCH 032/120] Made progress on a native testlet implementation Signed-off-by: Matt Oswalt --- agent/tasks/executetestrun.go | 33 ++++++++++++++++++++++------- agent/testing/testlets/ping/ping.go | 21 +++++++++--------- agent/testing/testlets/testlets.go | 10 ++++++++- 3 files changed, 45 insertions(+), 19 deletions(-) diff --git a/agent/tasks/executetestrun.go b/agent/tasks/executetestrun.go index 09904b0..c72ccb1 100644 --- a/agent/tasks/executetestrun.go +++ b/agent/tasks/executetestrun.go @@ -69,21 +69,38 @@ func (ett ExecuteTestRunTask) Run() error { // Specify size of wait group wg.Add(len(tr.Targets)) - // Determine if this is a native testlet - nativeTestlet, err := testlets.NewTestlet(tr.Testlet) - native := (err == nil) - // Execute testlets against all targets asynchronously for i := range tr.Targets { thisTarget := tr.Targets[i] - if native { + if testlets.IsNativeTestlet(tr.Testlet) { go func() { - // TODO(mierdin): Just calling a temporary method to ensure we can get to the native testlet. - // (it works!) - log.Error(nativeTestlet.Test()) + // TODO(mierdin): Something worried me here (can't remember what) regarding + // if only some agents were running native testlets, does this wait group methodology work? + // Sorry it's not clear, I have had a bit too much wine. + defer wg.Done() + + nativeTestlet, err := testlets.NewTestlet(tr.Testlet) + if err != nil { + //TODO(mierdin) do something + } + + metrics, err := nativeTestlet.Run("8.8.8.8", []string{"-c 10", "-s"}) + if err != nil { + //TODO(mierdin) do something + } + + // The metrics infrastructure requires that we collect metrics as a JSON string + // (which is a result of building non-native testlets in early versions of ToDD) + // So let's convert, and add to gatheredData + metrics_json, err := json.Marshal(metrics) + if err != nil { + //TODO(mierdin) do something + } + gatheredData[thisTarget] = string(metrics_json) + }() } else { // Generate path to testlet and make sure it exists. diff --git a/agent/testing/testlets/ping/ping.go b/agent/testing/testlets/ping/ping.go index f68b43b..51f29e6 100644 --- a/agent/testing/testlets/ping/ping.go +++ b/agent/testing/testlets/ping/ping.go @@ -13,24 +13,29 @@ package toddping // is being implemented so they can be broken out into their own repos. import ( + "time" + "github.com/Mierdin/todd/agent/testing/testlets" ) type PingTestlet struct{} func init() { + + // This is important - register the name of this testlet + // (the name the user will use in a testrun definition) testlets.Register("ping", &PingTestlet{}) } -// TODO(mierdin): Maybe consider running these asyc by default? Basically -// the "Run" function kicks back a channel of type map[string]string so when -// it's populated, it contains the metrics and you know it can stop - func (p PingTestlet) Run(target string, args []string) (map[string]string, error) { - // TODO(mierdin): Implement ping test here + // TODO(mierdin): Implement ping test here - this is just a mock + time.Sleep(3000 * time.Millisecond) + return map[string]string{ + "avg_latency_ms": "25.144", + "packet_loss_percentage": "0", + }, nil - return map[string]string{}, nil } func (p PingTestlet) Kill() error { @@ -40,7 +45,3 @@ func (p PingTestlet) Kill() error { return nil } - -func (p PingTestlet) Test() string { - return "trolololol" -} diff --git a/agent/testing/testlets/testlets.go b/agent/testing/testlets/testlets.go index 6ad65cf..64ed586 100644 --- a/agent/testing/testlets/testlets.go +++ b/agent/testing/testlets/testlets.go @@ -27,8 +27,16 @@ type Testlet interface { Run(string, []string) (map[string]string, error) Kill() error +} - Test() string //TODO(mierdin): Remove me +// IsNativeTestlet polls the list of registered native testlets, and returns +// true if the referenced name exists +func IsNativeTestlet(name string) bool { + if _, ok := testlets[name]; ok { + return true + } else { + return false + } } //NewTestlet produces a new testlet based on the "name" param From 5a3401f4f9a31ce67a213ad7302c6a42c581268d Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Fri, 29 Jul 2016 02:21:01 -0700 Subject: [PATCH 033/120] Implement async handling of testlet running Signed-off-by: Matt Oswalt --- agent/tasks/executetestrun.go | 7 ++- agent/testing/testlets/ping/ping.go | 29 ++++++----- agent/testing/testlets/testlets.go | 81 ++++++++++++++++++++++++++++- 3 files changed, 101 insertions(+), 16 deletions(-) diff --git a/agent/tasks/executetestrun.go b/agent/tasks/executetestrun.go index c72ccb1..b9210de 100644 --- a/agent/tasks/executetestrun.go +++ b/agent/tasks/executetestrun.go @@ -87,9 +87,11 @@ func (ett ExecuteTestRunTask) Run() error { //TODO(mierdin) do something } - metrics, err := nativeTestlet.Run("8.8.8.8", []string{"-c 10", "-s"}) + metrics, err := nativeTestlet.Run("8.8.8.8", []string{"-c 10", "-s"}, ett.TimeLimit) + //log.Error(nativeTestlet.RunFunction) if err != nil { - //TODO(mierdin) do something + log.Errorf("Testlet completed with error '%s'", err) + gatheredData[thisTarget] = "error" } // The metrics infrastructure requires that we collect metrics as a JSON string @@ -125,6 +127,7 @@ func (ett ExecuteTestRunTask) Run() error { // Execute collector cmd.Start() + // TODO(mierdin): Does this need to be a buffered channel? done := make(chan error, 1) go func() { done <- cmd.Wait() diff --git a/agent/testing/testlets/ping/ping.go b/agent/testing/testlets/ping/ping.go index 51f29e6..8ceb887 100644 --- a/agent/testing/testlets/ping/ping.go +++ b/agent/testing/testlets/ping/ping.go @@ -9,25 +9,38 @@ package toddping // Currently, each testlet is stored within the ToDD repo in order to // vet out the architecture, which means they are awkwardly placed // underneath the "testlets" directory together. However, this should be -// a tempoarary holding place, as the main effort around native testlets +// a temporary holding place, as the main effort around native testlets // is being implemented so they can be broken out into their own repos. import ( "time" + // log "github.com/Sirupsen/logrus" + "github.com/Mierdin/todd/agent/testing/testlets" ) -type PingTestlet struct{} +type PingTestlet struct { + testlets.BaseTestlet +} func init() { + var pt = PingTestlet{} + + // Ensure the RunFunction attribute is set correctly. + // This allows the underlying testlet infrastructure + // to know what function to call at runtime + pt.RunFunction = pt.RunTestlet + // This is important - register the name of this testlet // (the name the user will use in a testrun definition) - testlets.Register("ping", &PingTestlet{}) + testlets.Register("ping", &pt) } -func (p PingTestlet) Run(target string, args []string) (map[string]string, error) { +// RunTestlet implements the core logic of the testlet. Don't worry about running asynchronously, +// that's handled by the infrastructure. +func (p PingTestlet) RunTestlet(target string, args []string) (map[string]string, error) { // TODO(mierdin): Implement ping test here - this is just a mock time.Sleep(3000 * time.Millisecond) @@ -37,11 +50,3 @@ func (p PingTestlet) Run(target string, args []string) (map[string]string, error }, nil } - -func (p PingTestlet) Kill() error { - // TODO (mierdin): This will have to be coordinated with the task above. Basically - // you need a way to kill this testlet (and that's really only possible when running - // async) - - return nil -} diff --git a/agent/testing/testlets/testlets.go b/agent/testing/testlets/testlets.go index 64ed586..0ebb0c7 100644 --- a/agent/testing/testlets/testlets.go +++ b/agent/testing/testlets/testlets.go @@ -5,30 +5,104 @@ import ( "fmt" "sort" "sync" + "time" //"sync/atomic" + + log "github.com/Sirupsen/logrus" ) var ( testletsMu sync.RWMutex testlets = make(map[string]Testlet) + done = make(chan error) ) // Testlet defines what a testlet should look like if built in native // go and compiled with the agent type Testlet interface { + // Run is the "workflow" function for a testlet. It handles running + // the RunTestlet function asynchronously and managing the state therein. + // // Params are // target (string) // args ([]string) + // timeLimit (int in seconds) // // Returns: // metrics (map[string]interface{}) // (name of metric is key, value is metric value) - Run(string, []string) (map[string]string, error) + // + // Keep as much logic out of here as possible. All native testlets + // must support a "Kill" method, so it's best to implement core testlet + // logic in a separate function so that the Run and Kill commands can manage + // execution of that logic in a goroutine + Run(string, []string, int) (map[string]string, error) + // RunTestlet is designed to be the one-stop shop for testlet logic. + // The developer of a native testlet just needs to implement the testlet logic here, + // without worrying about things like managing goroutines or channels. That's all + // managed by the "Run" or "Kill" functions + RunTestlet(string, []string) (map[string]string, error) + // TODO(mierdin): is this really the best name for it? Maybe something that's less confusing, less like "Run" + + // All testlets must be able to stop operation when sent a Kill command. Kill() error } +type rtfunc func(target string, args []string) (map[string]string, error) + +type BaseTestlet struct { + + // rtfunc is a type that will store our RunTestlet function. It is the responsibility + // of the "child" testlet to set this value upon creation + RunFunction rtfunc +} + +// Run takes care of running the testlet function and managing it's operation given the parameters provided +func (b BaseTestlet) Run(target string, args []string, timeLimit int) (map[string]string, error) { + + var metrics map[string]string + + // TODO(mierdin): ensure channel is nil + // done = make(chan error) + + // TODO(mierdin): Based on experimentation, this will keep running even if this function returns. + // Need to be sure about how this ends. Also might want to evaluate the same for the existing non-native model, likely has the same issue + go func() { + theseMetrics, err := b.RunFunction(target, args) + metrics = theseMetrics //TODO(mierdin): Gross. + done <- err + }() + + // This select statement will block until one of these two conditions are met: + // - The testlet finishes, in which case the channel "done" will be receive a value + // - The configured time limit is exceeded (expected for testlets running in server mode) + select { + case <-time.After(time.Duration(timeLimit) * time.Second): + log.Debug("Successfully killed ") + return map[string]string{}, nil + + case err := <-done: + if err != nil { + return map[string]string{}, errors.New("testlet error") // TODO(mierdin): elaborate? + } else { + log.Debugf("Testlet completed without error") + return metrics, nil + } + } +} + +func (b BaseTestlet) Kill() error { + // TODO (mierdin): This will have to be coordinated with the task above. Basically + // you need a way to kill this testlet (and that's really only possible when running + // async) + + // Probably just want to set the channel to something so the select within "Run" will execute + + return nil +} + // IsNativeTestlet polls the list of registered native testlets, and returns // true if the referenced name exists func IsNativeTestlet(name string) bool { @@ -43,6 +117,9 @@ func IsNativeTestlet(name string) bool { func NewTestlet(name string) (Testlet, error) { if testlet, ok := testlets[name]; ok { + + // testlet.runFunction = testlet.run + return testlet, nil } else { return nil, errors.New( @@ -67,7 +144,7 @@ func Register(name string, testlet Testlet) error { return nil } -func unregisterAllDrivers() { +func unregisterAllTestlets() { testletsMu.Lock() defer testletsMu.Unlock() // For tests. From 7ee3fd61b723af4259abdb7aeaeca04ebc0aca26 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Sun, 31 Jul 2016 16:30:10 -0700 Subject: [PATCH 034/120] Further progress Signed-off-by: Matt Oswalt --- agent/testing/testlets/ping/ping.go | 57 ++++++++++++++++++++++++++--- agent/testing/testlets/testlets.go | 10 +++-- 2 files changed, 57 insertions(+), 10 deletions(-) diff --git a/agent/testing/testlets/ping/ping.go b/agent/testing/testlets/ping/ping.go index 8ceb887..42cc8d3 100644 --- a/agent/testing/testlets/ping/ping.go +++ b/agent/testing/testlets/ping/ping.go @@ -13,9 +13,10 @@ package toddping // is being implemented so they can be broken out into their own repos. import ( + "fmt" "time" - // log "github.com/Sirupsen/logrus" + log "github.com/Sirupsen/logrus" "github.com/Mierdin/todd/agent/testing/testlets" ) @@ -40,13 +41,57 @@ func init() { // RunTestlet implements the core logic of the testlet. Don't worry about running asynchronously, // that's handled by the infrastructure. -func (p PingTestlet) RunTestlet(target string, args []string) (map[string]string, error) { +func (p PingTestlet) RunTestlet(target string, args []string, kill chan (bool)) (map[string]string, error) { + + // Get number of pings + count := 3 //TODO(mierdin): need to parse from 'args', or if omitted, use a default value + + log.Error(args) + + var latencies []float32 + var replies int + + // Execute ping once per count + i := 0 + for i < count { + select { + case <-kill: + // Terminating early; return empty metrics + return map[string]string{}, nil + default: + + //log.Debugf("Executing ping #%d", i) + + // Mocked ping logic + latency, replyReceived := pingTemp(count) + + latencies = append(latencies, latency) + + if replyReceived { + replies += 1 + } + + i += 1 + time.Sleep(1000 * time.Millisecond) + + } + } + + // Calculate metrics + var latencyTotal float32 = 0 + for _, value := range latencies { + latencyTotal += value + } + avg_latency_ms := latencyTotal / float32(len(latencies)) + packet_loss := (float32(count) - float32(replies)) / float32(count) - // TODO(mierdin): Implement ping test here - this is just a mock - time.Sleep(3000 * time.Millisecond) return map[string]string{ - "avg_latency_ms": "25.144", - "packet_loss_percentage": "0", + "avg_latency_ms": fmt.Sprintf("%.2f", avg_latency_ms), + "packet_loss": fmt.Sprintf("%.2f", packet_loss), }, nil } + +func pingTemp(count int) (float32, bool) { + return float32(count) * 4.234, true +} diff --git a/agent/testing/testlets/testlets.go b/agent/testing/testlets/testlets.go index 0ebb0c7..4458892 100644 --- a/agent/testing/testlets/testlets.go +++ b/agent/testing/testlets/testlets.go @@ -14,7 +14,8 @@ import ( var ( testletsMu sync.RWMutex testlets = make(map[string]Testlet) - done = make(chan error) + done = make(chan error) // Used from within the goroutine to inform the infrastructure it has finished + kill = make(chan bool) // Used from outside the goroutine to inform the goroutine to stop ) // Testlet defines what a testlet should look like if built in native @@ -43,14 +44,14 @@ type Testlet interface { // The developer of a native testlet just needs to implement the testlet logic here, // without worrying about things like managing goroutines or channels. That's all // managed by the "Run" or "Kill" functions - RunTestlet(string, []string) (map[string]string, error) + RunTestlet(string, []string, chan bool) (map[string]string, error) // TODO(mierdin): is this really the best name for it? Maybe something that's less confusing, less like "Run" // All testlets must be able to stop operation when sent a Kill command. Kill() error } -type rtfunc func(target string, args []string) (map[string]string, error) +type rtfunc func(target string, args []string, kill chan bool) (map[string]string, error) type BaseTestlet struct { @@ -66,11 +67,12 @@ func (b BaseTestlet) Run(target string, args []string, timeLimit int) (map[strin // TODO(mierdin): ensure channel is nil // done = make(chan error) + // kill = make(chan bool) // TODO(mierdin): Based on experimentation, this will keep running even if this function returns. // Need to be sure about how this ends. Also might want to evaluate the same for the existing non-native model, likely has the same issue go func() { - theseMetrics, err := b.RunFunction(target, args) + theseMetrics, err := b.RunFunction(target, args, kill) metrics = theseMetrics //TODO(mierdin): Gross. done <- err }() From 62ba6c00811e0f1c4b717821f0bd81863d771831 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Mon, 1 Aug 2016 18:50:16 -0700 Subject: [PATCH 035/120] Catch-up commit Signed-off-by: Matt Oswalt --- .gitignore | 3 +- Makefile | 3 +- agent/testing/{testlets => bashtestlets}/http | 0 .../testing/{testlets => bashtestlets}/iperf | 0 .../_ping => bashtestlets/notatallping} | 0 agent/testing/testlets/ping/ping.go | 97 ------------------- assets/assets_unpack.go | 66 ++++++------- cmd/todd-agent/main.go | 2 +- cmd/todd-server/assets.go | 2 +- docs/nativetests.rst | 16 ++- scripts/buildtestlets.sh | 69 +++++++++++++ scripts/start-containers.sh | 2 +- 12 files changed, 124 insertions(+), 136 deletions(-) rename agent/testing/{testlets => bashtestlets}/http (100%) rename agent/testing/{testlets => bashtestlets}/iperf (100%) rename agent/testing/{testlets/_ping => bashtestlets/notatallping} (100%) delete mode 100644 agent/testing/testlets/ping/ping.go create mode 100755 scripts/buildtestlets.sh diff --git a/.gitignore b/.gitignore index 7c8cfbd..9896fa8 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ docs/_build/ preso_secret/ client/repeattest.sh *.out -*.DS_Store* \ No newline at end of file +*.DS_Store* +agent/testing/downloaded_testlets/ \ No newline at end of file diff --git a/Makefile b/Makefile index 7e21cf1..d5e11f3 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,7 @@ build: docker build -t mierdin/todd -f Dockerfile . compile: + ./scripts/buildtestlets.sh go install ./cmd/... install: configureenv @@ -27,7 +28,7 @@ update_deps: update_assets: go get -u github.com/jteeuwen/go-bindata/... - go-bindata -o assets/assets_unpack.go -pkg="assets" -prefix="agent" agent/testing/testlets/... agent/facts/collectors/... + go-bindata -o assets/assets_unpack.go -pkg="assets" -prefix="agent" agent/testing/bashtestlets/... agent/facts/collectors/... start: compile diff --git a/agent/testing/testlets/http b/agent/testing/bashtestlets/http similarity index 100% rename from agent/testing/testlets/http rename to agent/testing/bashtestlets/http diff --git a/agent/testing/testlets/iperf b/agent/testing/bashtestlets/iperf similarity index 100% rename from agent/testing/testlets/iperf rename to agent/testing/bashtestlets/iperf diff --git a/agent/testing/testlets/_ping b/agent/testing/bashtestlets/notatallping similarity index 100% rename from agent/testing/testlets/_ping rename to agent/testing/bashtestlets/notatallping diff --git a/agent/testing/testlets/ping/ping.go b/agent/testing/testlets/ping/ping.go deleted file mode 100644 index 42cc8d3..0000000 --- a/agent/testing/testlets/ping/ping.go +++ /dev/null @@ -1,97 +0,0 @@ -package toddping - -// NOTE ////////////////// -// -// This is a built-in testlet. Currently, the approach is to have each -// testlet under it's own package, which is explicitly imported under -// the ToDD agent's 'main' package. -// -// Currently, each testlet is stored within the ToDD repo in order to -// vet out the architecture, which means they are awkwardly placed -// underneath the "testlets" directory together. However, this should be -// a temporary holding place, as the main effort around native testlets -// is being implemented so they can be broken out into their own repos. - -import ( - "fmt" - "time" - - log "github.com/Sirupsen/logrus" - - "github.com/Mierdin/todd/agent/testing/testlets" -) - -type PingTestlet struct { - testlets.BaseTestlet -} - -func init() { - - var pt = PingTestlet{} - - // Ensure the RunFunction attribute is set correctly. - // This allows the underlying testlet infrastructure - // to know what function to call at runtime - pt.RunFunction = pt.RunTestlet - - // This is important - register the name of this testlet - // (the name the user will use in a testrun definition) - testlets.Register("ping", &pt) -} - -// RunTestlet implements the core logic of the testlet. Don't worry about running asynchronously, -// that's handled by the infrastructure. -func (p PingTestlet) RunTestlet(target string, args []string, kill chan (bool)) (map[string]string, error) { - - // Get number of pings - count := 3 //TODO(mierdin): need to parse from 'args', or if omitted, use a default value - - log.Error(args) - - var latencies []float32 - var replies int - - // Execute ping once per count - i := 0 - for i < count { - select { - case <-kill: - // Terminating early; return empty metrics - return map[string]string{}, nil - default: - - //log.Debugf("Executing ping #%d", i) - - // Mocked ping logic - latency, replyReceived := pingTemp(count) - - latencies = append(latencies, latency) - - if replyReceived { - replies += 1 - } - - i += 1 - time.Sleep(1000 * time.Millisecond) - - } - } - - // Calculate metrics - var latencyTotal float32 = 0 - for _, value := range latencies { - latencyTotal += value - } - avg_latency_ms := latencyTotal / float32(len(latencies)) - packet_loss := (float32(count) - float32(replies)) / float32(count) - - return map[string]string{ - "avg_latency_ms": fmt.Sprintf("%.2f", avg_latency_ms), - "packet_loss": fmt.Sprintf("%.2f", packet_loss), - }, nil - -} - -func pingTemp(count int) (float32, bool) { - return float32(count) * 4.234, true -} diff --git a/assets/assets_unpack.go b/assets/assets_unpack.go index 8f23ace..2a4a303 100644 --- a/assets/assets_unpack.go +++ b/assets/assets_unpack.go @@ -1,8 +1,8 @@ // Code generated by go-bindata. // sources: -// agent/testing/testlets/http -// agent/testing/testlets/iperf -// agent/testing/testlets/ping +// agent/testing/bashtestlets/http +// agent/testing/bashtestlets/iperf +// agent/testing/bashtestlets/notatallping // agent/facts/collectors/get_addresses // agent/facts/collectors/get_hostname // DO NOT EDIT! @@ -72,62 +72,62 @@ func (fi bindataFileInfo) Sys() interface{} { return nil } -var _testingTestletsHttp = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xa4\x97\x5f\x53\xdb\x38\x17\xc6\xef\xfd\x29\xce\x6b\xf2\x36\xa5\x03\x71\xc3\xc5\x5e\xd0\x81\x59\x1a\x42\xa1\x0d\x7f\x9a\xc0\xec\xee\x34\x9d\x8c\x63\x2b\x89\xa7\x8e\xe4\xb5\xe4\xb0\x2c\xf0\xdd\xf7\x48\xb2\x65\xc9\xa1\x21\xb3\xbb\x37\x6b\x1d\xff\xce\x63\xe9\xe8\x9c\x27\x74\xe7\x7f\x41\xc1\xf3\x60\x9a\xd0\x80\xd0\x15\x4c\x43\xbe\xf0\xbc\x1d\xe8\xb1\xec\x21\x4f\xe6\x0b\x01\x07\xef\xbb\xbf\xc0\x65\x28\x04\x5c\xf3\xfb\x30\x15\x1d\xb8\xe3\x04\x58\x0e\x4b\x16\x27\xb3\x24\x0a\x45\xc2\x28\xb0\x19\x88\x45\xc2\x31\x93\xb3\x22\x8f\x08\x44\x2c\x26\x90\x70\x98\xb3\x15\xc9\x29\x89\x61\xfa\x80\x04\x81\x34\x89\x08\x45\x81\x2c\x67\xab\x24\xc6\xf8\x82\xe4\xe4\x10\xf3\x16\x42\x64\xfc\x30\x08\xe6\x89\x58\x14\xd3\x4e\xc4\x96\xc1\x65\x42\xf2\x18\x37\x26\x58\x1c\x07\xd3\x94\x4d\x83\x65\xc8\x05\xc9\x83\xc1\x45\xaf\x7f\x35\xea\x7b\x5e\xb4\x20\xd1\x8f\xb7\xbb\xf0\xe8\x01\xfe\x47\xa2\x05\x03\x7f\x58\x50\x9a\xd0\x39\x24\x14\xd4\x6b\xb9\x51\xe2\x2b\x00\x45\x97\x21\x8d\x61\x7f\x05\x51\x91\xa7\x70\x1c\xc4\x64\x15\xd0\x22\x4d\xe1\xe0\xf8\x4d\x17\x9e\x9e\xe0\x51\xab\x1c\xbf\x39\x00\x5f\x31\x78\x06\xca\x04\xaa\x71\x11\xa6\x29\x89\x3b\xd0\xff\x2b\x11\xf8\x81\x8e\xff\x01\x08\x3e\x42\xf7\x03\x3c\x5b\xdf\xef\x99\x8f\xc2\xcd\xc9\x68\xd4\x3f\xd5\xdf\x56\xe8\x7b\xef\xd9\xf3\x92\x19\x7c\xfb\x06\xad\x2e\x1c\x1d\xe1\x37\x24\xed\xc3\xf7\xef\x1f\x90\xc2\x02\x51\xbd\x51\x19\xf5\x66\x89\xbc\x8a\x5b\xac\xda\x2c\xc9\xb9\x80\x30\x9f\x17\x4b\x42\xf1\x61\x86\x55\x50\xe5\x14\x84\x8b\x94\x08\xa0\xe1\x92\xc0\x7d\x82\x07\x39\x19\xfc\x76\xf2\xc7\x08\xa6\x44\xbf\xc7\x1c\x82\x77\x36\x62\x7b\xf2\x8b\x78\x98\x30\xbd\x0f\x1f\xb8\xf5\x72\x0f\x64\x49\xf0\x6c\x46\x9f\xc3\x2c\x67\x4b\x68\x1d\xa8\x37\x45\x06\x7c\xc1\x8a\x34\x96\x9a\x59\xc8\x39\x5e\x5a\x42\x05\x53\x12\x61\x96\x55\x55\xf5\xf0\x79\x82\x12\xfc\xc8\x6f\xfd\xea\xd7\xab\xd6\x63\xf5\xb8\xd3\x7a\x0f\xcf\xb0\x03\x43\xb2\xc4\xae\x80\x5b\x6b\xef\x2f\xe3\x5d\x07\x57\xbb\xc5\x82\x84\x42\xf5\x1a\x64\x0c\xb7\xb1\x07\x15\x6e\xed\x52\x1e\x46\x2c\x42\xd1\xe6\x90\x92\x99\xf0\x3c\x56\x88\xac\x10\x47\xad\xb7\xea\x4a\xf7\x39\xec\x33\xa8\xef\x7e\xff\x1e\xc6\xaa\xec\xed\xb1\x2e\xff\x98\x9e\xdf\xde\xde\xe0\x10\xc4\xe4\x10\xfe\xff\x28\x5b\x73\x22\x1b\xfa\x19\x26\xfd\xab\xd3\x13\x43\x0d\x18\xfb\x81\xe5\x11\xc9\x52\x71\xf2\xff\x13\x79\x9a\x54\xc5\x35\xfd\xd1\xd0\x3d\x46\x29\x89\x84\x8b\x47\x3a\xa8\xd9\x9e\x61\x6f\x72\xf2\xfb\x4c\xde\xb1\xcd\x66\x39\x11\x79\x48\x39\xbe\xd0\xfc\xa9\xe1\x47\x78\x97\x62\x3d\x83\xcb\xb0\x9b\xd3\x37\x39\xb7\x0c\x3b\xda\xe5\x85\x0c\x69\xee\xcc\xde\xb7\x90\x5d\x27\x1e\x32\x45\x46\x7a\x3d\x91\x6b\xcd\x7e\x6a\xd6\x4d\x9d\xc9\x2a\x9d\x75\xc6\x73\xc3\x5e\x15\xcb\x29\x6e\x18\xad\xa3\x4c\x40\x23\xe1\x32\x89\x16\xcb\x2a\x87\xeb\xa4\x8b\x17\x92\x86\x24\x4e\xf2\x46\x56\x5e\xc6\xca\xb4\xcf\x75\x7d\x92\xbf\x89\x4c\x8a\xd9\x3d\x4d\x59\x18\xcb\x04\x8e\xb1\x49\x15\xd0\x09\x5f\xd6\x12\x16\x24\x8c\x49\xce\x0d\xaf\xd7\x9a\x1e\xac\xd1\x39\xf9\xb3\xc0\xae\x36\x74\xb9\xd6\xf8\xe5\x1a\x5e\x64\xce\x5e\xf4\x52\xc3\x57\x06\x3e\x59\xcd\xe1\xb4\xdc\x25\x8c\x32\x42\x74\x82\x7c\x68\xec\xfe\xda\xc9\xb9\xcb\x5e\xca\xb0\xbf\x71\x63\xf8\xaa\x96\x6e\x37\x54\xd5\xd4\xf4\xd7\xba\x71\xf0\x25\xa0\x01\xa8\xee\x82\xaa\xbd\x36\x35\xdd\xb0\x1e\x19\x74\x70\xb8\x1b\x0e\xe0\x8c\x08\x74\x39\xb5\x33\x9c\xc9\x09\x99\xcd\xe4\x5d\xae\xca\x8e\x1a\x99\x84\x36\xce\x66\xab\xbb\xeb\x79\x66\x0a\x71\x8c\x95\xd3\xb6\xf4\x54\xc3\x13\x48\x4b\xda\xa7\xd0\xe6\x41\xe7\x9d\x35\xb9\xe3\xb7\x9d\x77\xe3\x5d\x3d\xb3\x9d\x77\xc1\xb8\x1b\x64\xed\x5d\xaf\x31\xa5\x9b\xd5\x9c\x09\xb7\xf4\x3e\x36\xf4\xca\x76\xdd\x2c\xe6\x1a\x80\xa5\xd6\x6b\xa8\x59\x83\xbe\x59\xd1\xb5\x09\x4b\xf1\xb4\xa1\xe8\xdc\xc9\x66\xcd\xa6\x95\x58\xaa\xfd\x86\xaa\xb2\x8b\xcd\x6a\xb6\xc9\x58\x4a\x67\xb5\x92\x6d\x27\xaf\xd6\xcf\x32\x22\x4b\xed\x53\xad\x66\x1b\xce\x56\x8d\x52\x5a\x95\xa5\x76\x5e\xab\xd9\x4e\xb4\x59\xed\x27\x66\x66\xc9\x5e\xb8\xb2\xc6\xaa\xb6\xd5\x75\xfd\xce\x12\xfe\x5c\x0b\x3b\x96\xf6\xca\x35\x37\x1d\xd1\xd1\xfc\xd2\xd0\xd4\xb6\xb7\x9d\xa2\xb1\x4c\x4b\x6f\xd0\xd0\x2b\x8d\x71\x3b\x41\xe3\xaa\x96\xe0\x65\x43\x50\xfb\xda\x76\x7a\x95\xed\x5a\x72\x57\x96\x9c\xe3\xac\x9b\x15\x5f\xf2\x66\x4b\xf5\xba\xa9\xba\xcd\x2e\xd7\xbd\xdb\x52\xbc\x69\x4c\x60\xd5\x45\x9b\x25\x1b\xf6\x6e\xe9\x7d\xfd\xf7\x3e\xf1\xb3\x5f\x01\x4b\x7d\x58\xab\x3b\x1e\xff\x8a\xe7\xae\xfd\x44\x58\x92\xa3\x5a\xd2\xc3\x3f\x7e\xf1\xef\xc7\x49\x1c\x8a\xf0\xa8\xad\xff\x09\xe0\x9b\x5f\x09\xff\xd0\x6f\xb7\xcc\xaa\xed\xef\xe9\xf7\x0d\xf7\x57\x54\x23\xe6\xb2\xe5\xf8\xd7\x60\x19\x70\x29\xcb\xb1\x6b\xd2\x0a\xba\xb4\x53\xe5\x9a\x77\xc2\x6e\x86\x72\xda\x9a\x54\x4b\x43\xd8\x0e\xaa\x18\x3b\x60\x28\xdb\x19\xed\xe2\xb8\xa7\xb1\x1d\x4f\x51\x76\xc0\xa1\x8c\x81\x19\xcc\x44\x0c\xe7\xf8\x91\xe2\x9c\x88\xcb\x69\xdf\xa8\x29\xbd\x76\x99\xd2\x0a\x6a\xa8\x0c\xb8\x94\x1e\xb3\x1a\xd2\xeb\x9a\x71\x26\x5c\x63\x4e\xa8\x41\xda\x72\x56\xc0\xbd\xa1\xea\xf0\xf5\x25\x55\x91\xff\x70\xf7\xce\xd4\x28\xd8\x89\xb4\x7d\xef\xb9\xed\xe9\x51\xb2\x86\xc1\xfb\x27\x00\x00\xff\xff\x75\x36\xff\x66\xd5\x0f\x00\x00") +var _testingBashtestletsHttp = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xa4\x97\x5f\x53\xdb\x38\x17\xc6\xef\xfd\x29\xce\x6b\xf2\x36\xa5\x03\x71\xc3\xc5\x5e\xd0\x81\x59\x1a\x42\xa1\x0d\x7f\x9a\xc0\xec\xee\x34\x9d\x8c\x63\x2b\x89\xa7\x8e\xe4\xb5\xe4\xb0\x2c\xf0\xdd\xf7\x48\xb2\x65\xc9\xa1\x21\xb3\xbb\x37\x6b\x1d\xff\xce\x63\xe9\xe8\x9c\x27\x74\xe7\x7f\x41\xc1\xf3\x60\x9a\xd0\x80\xd0\x15\x4c\x43\xbe\xf0\xbc\x1d\xe8\xb1\xec\x21\x4f\xe6\x0b\x01\x07\xef\xbb\xbf\xc0\x65\x28\x04\x5c\xf3\xfb\x30\x15\x1d\xb8\xe3\x04\x58\x0e\x4b\x16\x27\xb3\x24\x0a\x45\xc2\x28\xb0\x19\x88\x45\xc2\x31\x93\xb3\x22\x8f\x08\x44\x2c\x26\x90\x70\x98\xb3\x15\xc9\x29\x89\x61\xfa\x80\x04\x81\x34\x89\x08\x45\x81\x2c\x67\xab\x24\xc6\xf8\x82\xe4\xe4\x10\xf3\x16\x42\x64\xfc\x30\x08\xe6\x89\x58\x14\xd3\x4e\xc4\x96\xc1\x65\x42\xf2\x18\x37\x26\x58\x1c\x07\xd3\x94\x4d\x83\x65\xc8\x05\xc9\x83\xc1\x45\xaf\x7f\x35\xea\x7b\x5e\xb4\x20\xd1\x8f\xb7\xbb\xf0\xe8\x01\xfe\x47\xa2\x05\x03\x7f\x58\x50\x9a\xd0\x39\x24\x14\xd4\x6b\xb9\x51\xe2\x2b\x00\x45\x97\x21\x8d\x61\x7f\x05\x51\x91\xa7\x70\x1c\xc4\x64\x15\xd0\x22\x4d\xe1\xe0\xf8\x4d\x17\x9e\x9e\xe0\x51\xab\x1c\xbf\x39\x00\x5f\x31\x78\x06\xca\x04\xaa\x71\x11\xa6\x29\x89\x3b\xd0\xff\x2b\x11\xf8\x81\x8e\xff\x01\x08\x3e\x42\xf7\x03\x3c\x5b\xdf\xef\x99\x8f\xc2\xcd\xc9\x68\xd4\x3f\xd5\xdf\x56\xe8\x7b\xef\xd9\xf3\x92\x19\x7c\xfb\x06\xad\x2e\x1c\x1d\xe1\x37\x24\xed\xc3\xf7\xef\x1f\x90\xc2\x02\x51\xbd\x51\x19\xf5\x66\x89\xbc\x8a\x5b\xac\xda\x2c\xc9\xb9\x80\x30\x9f\x17\x4b\x42\xf1\x61\x86\x55\x50\xe5\x14\x84\x8b\x94\x08\xa0\xe1\x92\xc0\x7d\x82\x07\x39\x19\xfc\x76\xf2\xc7\x08\xa6\x44\xbf\xc7\x1c\x82\x77\x36\x62\x7b\xf2\x8b\x78\x98\x30\xbd\x0f\x1f\xb8\xf5\x72\x0f\x64\x49\xf0\x6c\x46\x9f\xc3\x2c\x67\x4b\x68\x1d\xa8\x37\x45\x06\x7c\xc1\x8a\x34\x96\x9a\x59\xc8\x39\x5e\x5a\x42\x05\x53\x12\x61\x96\x55\x55\xf5\xf0\x79\x82\x12\xfc\xc8\x6f\xfd\xea\xd7\xab\xd6\x63\xf5\xb8\xd3\x7a\x0f\xcf\xb0\x03\x43\xb2\xc4\xae\x80\x5b\x6b\xef\x2f\xe3\x5d\x07\x57\xbb\xc5\x82\x84\x42\xf5\x1a\x64\x0c\xb7\xb1\x07\x15\x6e\xed\x52\x1e\x46\x2c\x42\xd1\xe6\x90\x92\x99\xf0\x3c\x56\x88\xac\x10\x47\xad\xb7\xea\x4a\xf7\x39\xec\x33\xa8\xef\x7e\xff\x1e\xc6\xaa\xec\xed\xb1\x2e\xff\x98\x9e\xdf\xde\xde\xe0\x10\xc4\xe4\x10\xfe\xff\x28\x5b\x73\x22\x1b\xfa\x19\x26\xfd\xab\xd3\x13\x43\x0d\x18\xfb\x81\xe5\x11\xc9\x52\x71\xf2\xff\x13\x79\x9a\x54\xc5\x35\xfd\xd1\xd0\x3d\x46\x29\x89\x84\x8b\x47\x3a\xa8\xd9\x9e\x61\x6f\x72\xf2\xfb\x4c\xde\xb1\xcd\x66\x39\x11\x79\x48\x39\xbe\xd0\xfc\xa9\xe1\x47\x78\x97\x62\x3d\x83\xcb\xb0\x9b\xd3\x37\x39\xb7\x0c\x3b\xda\xe5\x85\x0c\x69\xee\xcc\xde\xb7\x90\x5d\x27\x1e\x32\x45\x46\x7a\x3d\x91\x6b\xcd\x7e\x6a\xd6\x4d\x9d\xc9\x2a\x9d\x75\xc6\x73\xc3\x5e\x15\xcb\x29\x6e\x18\xad\xa3\x4c\x40\x23\xe1\x32\x89\x16\xcb\x2a\x87\xeb\xa4\x8b\x17\x92\x86\x24\x4e\xf2\x46\x56\x5e\xc6\xca\xb4\xcf\x75\x7d\x92\xbf\x89\x4c\x8a\xd9\x3d\x4d\x59\x18\xcb\x04\x8e\xb1\x49\x15\xd0\x09\x5f\xd6\x12\x16\x24\x8c\x49\xce\x0d\xaf\xd7\x9a\x1e\xac\xd1\x39\xf9\xb3\xc0\xae\x36\x74\xb9\xd6\xf8\xe5\x1a\x5e\x64\xce\x5e\xf4\x52\xc3\x57\x06\x3e\x59\xcd\xe1\xb4\xdc\x25\x8c\x32\x42\x74\x82\x7c\x68\xec\xfe\xda\xc9\xb9\xcb\x5e\xca\xb0\xbf\x71\x63\xf8\xaa\x96\x6e\x37\x54\xd5\xd4\xf4\xd7\xba\x71\xf0\x25\xa0\x01\xa8\xee\x82\xaa\xbd\x36\x35\xdd\xb0\x1e\x19\x74\x70\xb8\x1b\x0e\xe0\x8c\x08\x74\x39\xb5\x33\x9c\xc9\x09\x99\xcd\xe4\x5d\xae\xca\x8e\x1a\x99\x84\x36\xce\x66\xab\xbb\xeb\x79\x66\x0a\x71\x8c\x95\xd3\xb6\xf4\x54\xc3\x13\x48\x4b\xda\xa7\xd0\xe6\x41\xe7\x9d\x35\xb9\xe3\xb7\x9d\x77\xe3\x5d\x3d\xb3\x9d\x77\xc1\xb8\x1b\x64\xed\x5d\xaf\x31\xa5\x9b\xd5\x9c\x09\xb7\xf4\x3e\x36\xf4\xca\x76\xdd\x2c\xe6\x1a\x80\xa5\xd6\x6b\xa8\x59\x83\xbe\x59\xd1\xb5\x09\x4b\xf1\xb4\xa1\xe8\xdc\xc9\x66\xcd\xa6\x95\x58\xaa\xfd\x86\xaa\xb2\x8b\xcd\x6a\xb6\xc9\x58\x4a\x67\xb5\x92\x6d\x27\xaf\xd6\xcf\x32\x22\x4b\xed\x53\xad\x66\x1b\xce\x56\x8d\x52\x5a\x95\xa5\x76\x5e\xab\xd9\x4e\xb4\x59\xed\x27\x66\x66\xc9\x5e\xb8\xb2\xc6\xaa\xb6\xd5\x75\xfd\xce\x12\xfe\x5c\x0b\x3b\x96\xf6\xca\x35\x37\x1d\xd1\xd1\xfc\xd2\xd0\xd4\xb6\xb7\x9d\xa2\xb1\x4c\x4b\x6f\xd0\xd0\x2b\x8d\x71\x3b\x41\xe3\xaa\x96\xe0\x65\x43\x50\xfb\xda\x76\x7a\x95\xed\x5a\x72\x57\x96\x9c\xe3\xac\x9b\x15\x5f\xf2\x66\x4b\xf5\xba\xa9\xba\xcd\x2e\xd7\xbd\xdb\x52\xbc\x69\x4c\x60\xd5\x45\x9b\x25\x1b\xf6\x6e\xe9\x7d\xfd\xf7\x3e\xf1\xb3\x5f\x01\x4b\x7d\x58\xab\x3b\x1e\xff\x8a\xe7\xae\xfd\x44\x58\x92\xa3\x5a\xd2\xc3\x3f\x7e\xf1\xef\xc7\x49\x1c\x8a\xf0\xa8\xad\xff\x09\xe0\x9b\x5f\x09\xff\xd0\x6f\xb7\xcc\xaa\xed\xef\xe9\xf7\x0d\xf7\x57\x54\x23\xe6\xb2\xe5\xf8\xd7\x60\x19\x70\x29\xcb\xb1\x6b\xd2\x0a\xba\xb4\x53\xe5\x9a\x77\xc2\x6e\x86\x72\xda\x9a\x54\x4b\x43\xd8\x0e\xaa\x18\x3b\x60\x28\xdb\x19\xed\xe2\xb8\xa7\xb1\x1d\x4f\x51\x76\xc0\xa1\x8c\x81\x19\xcc\x44\x0c\xe7\xf8\x91\xe2\x9c\x88\xcb\x69\xdf\xa8\x29\xbd\x76\x99\xd2\x0a\x6a\xa8\x0c\xb8\x94\x1e\xb3\x1a\xd2\xeb\x9a\x71\x26\x5c\x63\x4e\xa8\x41\xda\x72\x56\xc0\xbd\xa1\xea\xf0\xf5\x25\x55\x91\xff\x70\xf7\xce\xd4\x28\xd8\x89\xb4\x7d\xef\xb9\xed\xe9\x51\xb2\x86\xc1\xfb\x27\x00\x00\xff\xff\x75\x36\xff\x66\xd5\x0f\x00\x00") -func testingTestletsHttpBytes() ([]byte, error) { +func testingBashtestletsHttpBytes() ([]byte, error) { return bindataRead( - _testingTestletsHttp, - "testing/testlets/http", + _testingBashtestletsHttp, + "testing/bashtestlets/http", ) } -func testingTestletsHttp() (*asset, error) { - bytes, err := testingTestletsHttpBytes() +func testingBashtestletsHttp() (*asset, error) { + bytes, err := testingBashtestletsHttpBytes() if err != nil { return nil, err } - info := bindataFileInfo{name: "testing/testlets/http", size: 4053, mode: os.FileMode(493), modTime: time.Unix(1462644938, 0)} + info := bindataFileInfo{name: "testing/bashtestlets/http", size: 4053, mode: os.FileMode(493), modTime: time.Unix(1463727404, 0)} a := &asset{bytes: bytes, info: info} return a, nil } -var _testingTestletsIperf = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x94\x54\x4d\x6f\xe3\x36\x10\xbd\xeb\x57\x4c\x19\x63\x95\x04\x8e\x15\xe7\xd0\x43\x02\x07\x4d\xd3\x1c\x02\x6c\xb6\xc1\x3a\xc5\xa2\xd8\x2c\x0c\x4a\x1a\x59\x6c\x24\x52\x20\x47\x56\x5d\xc7\xff\xbd\x43\xd1\x5f\xbb\xf0\x65\xed\x0b\xcd\x99\x79\x7c\xef\xcd\x8c\x4f\x7e\x49\x5a\x67\x93\x54\xe9\x04\xf5\x02\x52\xe9\xca\x28\x3a\x81\x7b\xd3\x2c\xad\x9a\x97\x04\x57\x97\xe3\x5f\xe1\x49\x12\xc1\x9f\xae\x93\x15\x8d\xe0\x2f\x87\x60\x2c\xd4\x26\x57\x85\xca\x24\x29\xa3\xc1\x14\x40\xa5\x72\x5c\xe9\x4c\x6b\x33\x84\xcc\xe4\x08\xca\xc1\xdc\x2c\xd0\x6a\xcc\x21\x5d\x72\x06\x42\xa5\x32\xd4\x0c\xd0\x58\xb3\x50\x39\xdf\x97\x68\xf1\x9a\xeb\x4a\xa2\xc6\x5d\x27\xc9\x5c\x51\xd9\xa6\xa3\xcc\xd4\xc9\x93\x42\x9b\x33\x31\x32\x79\x9e\xa4\x95\x49\x93\x5a\x3a\x42\x9b\x7c\x7c\xbc\x7f\xf8\x34\x7d\x88\xa2\xac\xc4\xec\xed\xf4\x0c\x56\x11\xf0\x07\xb3\xd2\x80\xf8\xdc\x6a\xad\xf4\x1c\x94\x86\x3e\xec\x89\xa2\xe8\x13\x18\xb4\x96\x3a\x87\x8b\x05\xa8\x06\x6d\x01\xb7\x49\x8e\x8b\x44\xb7\x55\x05\x57\xb7\x1f\xc6\xf0\xfe\x0e\xab\x00\x73\xfb\xe1\x0a\x44\x48\x62\x15\xda\x10\xe3\x39\x92\x55\x85\xf9\x08\x1e\xfe\x55\xc4\x4f\x8c\xc4\x0d\x20\x1f\x61\x7c\x03\xeb\x03\x06\xf7\xbb\x67\xe1\xf9\x6e\x3a\x7d\xf8\x23\xbc\xde\xa7\x5e\x46\xeb\x28\x52\x05\x7c\xfd\x0a\x83\x31\x4c\x26\x20\x7a\x92\x02\xbe\x7d\xbb\xe1\x2c\xb6\x48\x07\xaa\xfe\x36\x2a\x94\x6f\xc6\x0b\xfb\x56\x28\xeb\x08\xa4\x9d\xb7\x35\x6a\x3e\x14\xec\x43\x6f\x28\xa1\xa3\x0a\x09\xb4\xac\x11\x3a\xc5\x4a\xee\x3e\x7e\xb9\xfb\x7b\x0a\x29\x86\x38\xd7\x20\x77\x6d\x6a\x86\xfe\x45\x16\x23\xab\x4e\x2e\xdd\x41\x70\x08\xde\x14\xd6\xb6\xc3\x77\x50\x58\x53\xc3\xe0\xaa\x8f\xb4\x0d\xb8\xd2\xb4\x55\xee\x31\x1b\xe9\x1c\xb7\x4d\x69\x32\x3d\x84\x6c\x9a\xad\xaf\x11\x9f\x67\x0c\xe1\x26\x62\xf0\x9b\xd8\xff\x1a\xac\xb6\xc7\x93\xc1\x25\xac\xe1\x04\x3e\x63\xcd\x73\x01\x2f\x07\xdc\x8f\xa7\x8f\xbf\x4b\xef\xd9\xb2\x21\x92\xfa\x69\x83\xc6\x30\x8d\x21\x6c\xd3\x0f\x58\x7a\x31\x54\x4a\x8a\x1d\x54\x58\x50\x70\x91\x2b\xb6\x6e\xb5\x0e\xdd\x46\x3d\xb8\x36\x75\xa4\xa8\xf5\x83\x3c\xe4\xf9\xe5\x0a\x5f\x57\xcb\x37\xe4\x98\xc5\x1e\x08\xb8\x67\xdc\x3e\xee\xa1\x63\x77\x78\xb6\x7a\xed\xfc\xe8\x10\x3a\x04\x8b\x4d\x25\x79\xea\x39\xa3\xe3\xf9\x0d\xc1\x8c\x5a\x59\x6d\x1f\x79\x7c\x1e\x1d\x28\x3c\xed\x07\x65\xb0\x23\xfe\x0e\xde\xd3\xd8\x25\xab\xd5\xb6\x60\xbd\x4e\x62\x31\x18\x8b\x38\x89\xcf\x3c\xfd\x4f\x86\x36\x4c\x3a\x8c\x99\x54\x61\x6c\xe6\xc7\xbc\xc6\xb9\x4c\x15\xeb\xea\x37\x89\x85\xf9\x4b\x71\x51\x40\x2d\xbc\x16\x26\x97\x49\x0d\xa5\x5c\xf8\x9d\xc3\x5c\x65\x24\xd3\xca\xb7\xd1\xfa\xcc\x11\x7c\xe1\x73\xeb\xfd\xc4\x3a\xf8\x8a\x80\xdc\x73\x67\xc2\x63\x52\x2f\xb9\xbd\xba\xe0\xbd\xf5\x23\xef\x8d\xb3\xfb\xe5\xed\xc9\x77\x46\xc7\xb4\x4b\xda\x1a\xa0\x5c\x10\x6c\x5b\x3d\xdb\xcc\xc7\x64\xb3\x50\x7b\xdd\x9e\x26\x88\x28\x32\x2d\x31\x09\xf6\x65\xf0\x43\x09\x4b\xa7\xac\x99\x75\x4a\xe7\xa6\x9b\x39\xf5\x1f\xce\xea\x74\x49\xb8\xb3\x30\x54\xb2\x81\x64\x21\x7e\xd5\x31\xf8\x6f\xb0\xf3\x42\x7b\x47\x47\xe7\x2f\xf7\xcf\x10\x00\xc0\x03\x5c\xc3\xeb\xe9\xe8\xfc\xf5\x6c\x74\x9e\xbc\x8e\x93\x26\xde\x9b\x0f\x4f\xbf\x33\x34\xdf\xb3\xe5\x00\x7e\x62\xd8\x80\x8a\xff\x72\xfa\x04\xde\x7e\xc2\x7e\xf9\xc2\x98\x79\xd1\x3c\x69\x1a\x33\x74\x4e\xda\x25\xa4\xcc\xe3\x91\x7d\xd8\xc4\x1c\xb2\xa3\xbc\x24\xbe\x99\xb4\xdb\x60\xa3\xd1\x5f\x76\xc6\xbe\xc1\x3f\x2d\x5f\x78\x47\x30\xa3\x6a\x19\x91\x95\xda\x15\x68\x83\x42\xf7\x33\x12\x1d\x66\x70\x1e\x64\x05\x11\x6e\xab\xee\x2c\x4a\xd9\xc6\x4e\xe5\x54\x32\xae\x22\x37\xe3\xdc\x9f\x81\x0e\x70\x7b\x74\x8f\xb1\x07\x8f\x38\xc6\xeb\x32\xcb\x25\xc9\x49\xbc\x12\x47\x9b\x25\xae\x45\x3c\x38\x1a\x89\xc5\x10\xc4\x0f\xba\x43\xf6\xf7\x77\x7d\xde\x11\x21\x7d\xee\x91\xfb\x58\xac\xe3\x28\x48\x3c\x20\x18\xfd\x1f\x00\x00\xff\xff\x4d\x99\x39\x1a\xed\x06\x00\x00") +var _testingBashtestletsIperf = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x94\x54\x4d\x6f\xe3\x36\x10\xbd\xeb\x57\x4c\x19\x63\x95\x04\x8e\x15\xe7\xd0\x43\x02\x07\x4d\xd3\x1c\x02\x6c\xb6\xc1\x3a\xc5\xa2\xd8\x2c\x0c\x4a\x1a\x59\x6c\x24\x52\x20\x47\x56\x5d\xc7\xff\xbd\x43\xd1\x5f\xbb\xf0\x65\xed\x0b\xcd\x99\x79\x7c\xef\xcd\x8c\x4f\x7e\x49\x5a\x67\x93\x54\xe9\x04\xf5\x02\x52\xe9\xca\x28\x3a\x81\x7b\xd3\x2c\xad\x9a\x97\x04\x57\x97\xe3\x5f\xe1\x49\x12\xc1\x9f\xae\x93\x15\x8d\xe0\x2f\x87\x60\x2c\xd4\x26\x57\x85\xca\x24\x29\xa3\xc1\x14\x40\xa5\x72\x5c\xe9\x4c\x6b\x33\x84\xcc\xe4\x08\xca\xc1\xdc\x2c\xd0\x6a\xcc\x21\x5d\x72\x06\x42\xa5\x32\xd4\x0c\xd0\x58\xb3\x50\x39\xdf\x97\x68\xf1\x9a\xeb\x4a\xa2\xc6\x5d\x27\xc9\x5c\x51\xd9\xa6\xa3\xcc\xd4\xc9\x93\x42\x9b\x33\x31\x32\x79\x9e\xa4\x95\x49\x93\x5a\x3a\x42\x9b\x7c\x7c\xbc\x7f\xf8\x34\x7d\x88\xa2\xac\xc4\xec\xed\xf4\x0c\x56\x11\xf0\x07\xb3\xd2\x80\xf8\xdc\x6a\xad\xf4\x1c\x94\x86\x3e\xec\x89\xa2\xe8\x13\x18\xb4\x96\x3a\x87\x8b\x05\xa8\x06\x6d\x01\xb7\x49\x8e\x8b\x44\xb7\x55\x05\x57\xb7\x1f\xc6\xf0\xfe\x0e\xab\x00\x73\xfb\xe1\x0a\x44\x48\x62\x15\xda\x10\xe3\x39\x92\x55\x85\xf9\x08\x1e\xfe\x55\xc4\x4f\x8c\xc4\x0d\x20\x1f\x61\x7c\x03\xeb\x03\x06\xf7\xbb\x67\xe1\xf9\x6e\x3a\x7d\xf8\x23\xbc\xde\xa7\x5e\x46\xeb\x28\x52\x05\x7c\xfd\x0a\x83\x31\x4c\x26\x20\x7a\x92\x02\xbe\x7d\xbb\xe1\x2c\xb6\x48\x07\xaa\xfe\x36\x2a\x94\x6f\xc6\x0b\xfb\x56\x28\xeb\x08\xa4\x9d\xb7\x35\x6a\x3e\x14\xec\x43\x6f\x28\xa1\xa3\x0a\x09\xb4\xac\x11\x3a\xc5\x4a\xee\x3e\x7e\xb9\xfb\x7b\x0a\x29\x86\x38\xd7\x20\x77\x6d\x6a\x86\xfe\x45\x16\x23\xab\x4e\x2e\xdd\x41\x70\x08\xde\x14\xd6\xb6\xc3\x77\x50\x58\x53\xc3\xe0\xaa\x8f\xb4\x0d\xb8\xd2\xb4\x55\xee\x31\x1b\xe9\x1c\xb7\x4d\x69\x32\x3d\x84\x6c\x9a\xad\xaf\x11\x9f\x67\x0c\xe1\x26\x62\xf0\x9b\xd8\xff\x1a\xac\xb6\xc7\x93\xc1\x25\xac\xe1\x04\x3e\x63\xcd\x73\x01\x2f\x07\xdc\x8f\xa7\x8f\xbf\x4b\xef\xd9\xb2\x21\x92\xfa\x69\x83\xc6\x30\x8d\x21\x6c\xd3\x0f\x58\x7a\x31\x54\x4a\x8a\x1d\x54\x58\x50\x70\x91\x2b\xb6\x6e\xb5\x0e\xdd\x46\x3d\xb8\x36\x75\xa4\xa8\xf5\x83\x3c\xe4\xf9\xe5\x0a\x5f\x57\xcb\x37\xe4\x98\xc5\x1e\x08\xb8\x67\xdc\x3e\xee\xa1\x63\x77\x78\xb6\x7a\xed\xfc\xe8\x10\x3a\x04\x8b\x4d\x25\x79\xea\x39\xa3\xe3\xf9\x0d\xc1\x8c\x5a\x59\x6d\x1f\x79\x7c\x1e\x1d\x28\x3c\xed\x07\x65\xb0\x23\xfe\x0e\xde\xd3\xd8\x25\xab\xd5\xb6\x60\xbd\x4e\x62\x31\x18\x8b\x38\x89\xcf\x3c\xfd\x4f\x86\x36\x4c\x3a\x8c\x99\x54\x61\x6c\xe6\xc7\xbc\xc6\xb9\x4c\x15\xeb\xea\x37\x89\x85\xf9\x4b\x71\x51\x40\x2d\xbc\x16\x26\x97\x49\x0d\xa5\x5c\xf8\x9d\xc3\x5c\x65\x24\xd3\xca\xb7\xd1\xfa\xcc\x11\x7c\xe1\x73\xeb\xfd\xc4\x3a\xf8\x8a\x80\xdc\x73\x67\xc2\x63\x52\x2f\xb9\xbd\xba\xe0\xbd\xf5\x23\xef\x8d\xb3\xfb\xe5\xed\xc9\x77\x46\xc7\xb4\x4b\xda\x1a\xa0\x5c\x10\x6c\x5b\x3d\xdb\xcc\xc7\x64\xb3\x50\x7b\xdd\x9e\x26\x88\x28\x32\x2d\x31\x09\xf6\x65\xf0\x43\x09\x4b\xa7\xac\x99\x75\x4a\xe7\xa6\x9b\x39\xf5\x1f\xce\xea\x74\x49\xb8\xb3\x30\x54\xb2\x81\x64\x21\x7e\xd5\x31\xf8\x6f\xb0\xf3\x42\x7b\x47\x47\xe7\x2f\xf7\xcf\x10\x00\xc0\x03\x5c\xc3\xeb\xe9\xe8\xfc\xf5\x6c\x74\x9e\xbc\x8e\x93\x26\xde\x9b\x0f\x4f\xbf\x33\x34\xdf\xb3\xe5\x00\x7e\x62\xd8\x80\x8a\xff\x72\xfa\x04\xde\x7e\xc2\x7e\xf9\xc2\x98\x79\xd1\x3c\x69\x1a\x33\x74\x4e\xda\x25\xa4\xcc\xe3\x91\x7d\xd8\xc4\x1c\xb2\xa3\xbc\x24\xbe\x99\xb4\xdb\x60\xa3\xd1\x5f\x76\xc6\xbe\xc1\x3f\x2d\x5f\x78\x47\x30\xa3\x6a\x19\x91\x95\xda\x15\x68\x83\x42\xf7\x33\x12\x1d\x66\x70\x1e\x64\x05\x11\x6e\xab\xee\x2c\x4a\xd9\xc6\x4e\xe5\x54\x32\xae\x22\x37\xe3\xdc\x9f\x81\x0e\x70\x7b\x74\x8f\xb1\x07\x8f\x38\xc6\xeb\x32\xcb\x25\xc9\x49\xbc\x12\x47\x9b\x25\xae\x45\x3c\x38\x1a\x89\xc5\x10\xc4\x0f\xba\x43\xf6\xf7\x77\x7d\xde\x11\x21\x7d\xee\x91\xfb\x58\xac\xe3\x28\x48\x3c\x20\x18\xfd\x1f\x00\x00\xff\xff\x4d\x99\x39\x1a\xed\x06\x00\x00") -func testingTestletsIperfBytes() ([]byte, error) { +func testingBashtestletsIperfBytes() ([]byte, error) { return bindataRead( - _testingTestletsIperf, - "testing/testlets/iperf", + _testingBashtestletsIperf, + "testing/bashtestlets/iperf", ) } -func testingTestletsIperf() (*asset, error) { - bytes, err := testingTestletsIperfBytes() +func testingBashtestletsIperf() (*asset, error) { + bytes, err := testingBashtestletsIperfBytes() if err != nil { return nil, err } - info := bindataFileInfo{name: "testing/testlets/iperf", size: 1773, mode: os.FileMode(493), modTime: time.Unix(1462644938, 0)} + info := bindataFileInfo{name: "testing/bashtestlets/iperf", size: 1773, mode: os.FileMode(493), modTime: time.Unix(1463727404, 0)} a := &asset{bytes: bytes, info: info} return a, nil } -var _testingTestletsPing = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x94\x93\x4f\x4f\xdb\x4a\x14\xc5\xf7\xf3\x29\xce\x1b\xfc\x70\x82\x20\x43\xb2\x78\x0b\x50\xd0\x43\xbc\x2c\x9e\x04\x2d\x6a\x5a\x55\x15\x41\xd1\xc4\xbe\x89\x47\xd8\x33\x96\x67\x1c\x1a\x25\x7c\xf7\x5e\xdb\x05\xac\xaa\x9b\xc2\xe6\x66\xce\xb9\xbf\xfb\x2f\x39\xfa\x4b\xd5\xbe\x52\x2b\x63\x15\xd9\x2d\x56\xda\x67\x42\x1c\xe1\xc6\x95\xbb\xca\x6c\xb2\x80\xc9\xf9\xf8\x1f\xdc\xe9\x10\xf0\xd1\x3f\xeb\x3c\x8c\xf0\xc5\x13\x5c\x85\xc2\xa5\x66\x6d\x12\x1d\x8c\xb3\x70\x6b\x84\xcc\x78\xce\xf4\xae\xae\x12\x42\xe2\x52\x82\xf1\xd8\xb8\x2d\x55\x96\x52\xac\x76\xec\x20\xe4\x26\x21\xcb\x80\xb2\x72\x5b\x93\xf2\x7b\x46\x15\x5d\x70\x5e\x16\x42\xe9\x2f\x94\xda\x98\x90\xd5\xab\x51\xe2\x0a\x75\x67\xa8\x4a\xb9\xb1\xe0\xd2\x54\xad\x72\xb7\x52\x85\xf6\x81\x2a\x75\xfb\xff\xcd\xec\xc3\x7c\x26\x44\x92\x51\xf2\x34\x18\x62\x2f\xc0\x7f\x94\x64\x0e\xf2\x53\x6d\xad\xb1\x1b\x18\x8b\x56\x6e\x1a\x25\xd9\x1a\x18\x5a\x68\x9b\xe2\x6c\x8b\xb2\xb1\x5c\xa9\x94\xb6\xca\xd6\x79\x8e\xc9\xd5\xf1\x18\x87\x03\xf6\x1d\xe5\xea\x78\x02\x79\xdf\x62\x3c\xac\x0b\x4c\xf3\x41\xe7\x39\xa5\x23\xcc\xbe\x9b\xc0\xca\x48\x5e\x82\x38\xc4\xf8\x12\x2f\xbd\xfa\x37\x6f\x45\x71\x7f\x3d\x9f\xcf\xfe\xeb\x6a\xb7\xd6\x73\xf1\x22\x84\x59\xe3\xe1\x01\xd1\x18\xd3\x29\x64\xdb\xa2\xc4\xe3\xe3\x25\xbb\x78\x41\xb6\x6b\xb4\x79\x15\x6b\xd3\x9c\xe2\x33\x6f\x6d\x6d\x2a\x1f\xa0\xab\x4d\x5d\x90\xe5\x60\xcd\x5b\x68\xd7\x19\xc8\x87\x9c\x02\xac\x2e\x08\xcf\x86\x07\xb9\xbe\xfd\x7a\xfd\x6d\x8e\x15\x75\x3a\xe7\x10\xdf\x6c\xee\x4e\x9b\x8a\x3c\x8c\xce\x9f\xf5\xce\xf7\xc4\x53\x34\x2b\xe1\xd9\xde\xf8\x1e\xeb\xca\x15\x88\x26\xad\x52\x97\xf0\x99\xab\xf3\xb4\x61\x96\xda\x7b\x3e\x9a\xb1\xc1\xb5\x08\x5d\x96\xaf\x5b\x15\x1c\x2f\x19\xe1\xa7\x32\xfa\x57\xbe\x7f\x8a\xf6\xaf\xe1\x51\x74\xce\x9b\xfa\xad\x30\x66\xa1\x55\xaa\xda\x2e\x7f\x02\xa7\xb2\x3d\x52\xf4\xea\xe2\x01\xa4\x10\xae\x0e\x65\x1d\xa6\xd1\x20\xfa\xc5\x3e\x14\xa2\xd4\xc9\x13\x85\x65\xee\x3c\xd3\x07\xed\x39\xa2\xce\x8f\x03\x42\x85\x78\x61\x63\x34\xff\x07\x34\x53\x9c\x59\xc4\x5e\x8d\x4e\xb0\x18\x8c\x4e\x16\xc3\xbf\xd1\xe5\xa3\xc9\x1f\x9d\xa8\xc5\x58\x95\xf1\x50\xe8\xed\x66\x99\xeb\x40\x36\xd9\xfd\x09\x94\xbf\x59\x98\x82\xb9\xaa\xa3\x2f\x54\x13\xbf\x63\x05\x9f\x8e\x2f\xb7\x4c\x75\xd0\xd3\x78\x2f\x7b\xbd\x2f\x4b\xe2\x9f\x90\x0d\x7a\x43\xf2\x42\xc6\x51\x4f\x8a\xe5\x29\x64\xaf\xa3\x65\xe1\x5b\x4b\xef\x29\x96\x2f\xb1\xe8\xda\xec\x95\xf8\x11\x00\x00\xff\xff\x84\x27\x4a\xf0\xe8\x03\x00\x00") +var _testingBashtestletsNotatallping = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x94\x93\x4f\x4f\xdb\x4a\x14\xc5\xf7\xf3\x29\xce\x1b\xfc\x70\x82\x20\x43\xb2\x78\x0b\x50\xd0\x43\xbc\x2c\x9e\x04\x2d\x6a\x5a\x55\x15\x41\xd1\xc4\xbe\x89\x47\xd8\x33\x96\x67\x1c\x1a\x25\x7c\xf7\x5e\xdb\x05\xac\xaa\x9b\xc2\xe6\x66\xce\xb9\xbf\xfb\x2f\x39\xfa\x4b\xd5\xbe\x52\x2b\x63\x15\xd9\x2d\x56\xda\x67\x42\x1c\xe1\xc6\x95\xbb\xca\x6c\xb2\x80\xc9\xf9\xf8\x1f\xdc\xe9\x10\xf0\xd1\x3f\xeb\x3c\x8c\xf0\xc5\x13\x5c\x85\xc2\xa5\x66\x6d\x12\x1d\x8c\xb3\x70\x6b\x84\xcc\x78\xce\xf4\xae\xae\x12\x42\xe2\x52\x82\xf1\xd8\xb8\x2d\x55\x96\x52\xac\x76\xec\x20\xe4\x26\x21\xcb\x80\xb2\x72\x5b\x93\xf2\x7b\x46\x15\x5d\x70\x5e\x16\x42\xe9\x2f\x94\xda\x98\x90\xd5\xab\x51\xe2\x0a\x75\x67\xa8\x4a\xb9\xb1\xe0\xd2\x54\xad\x72\xb7\x52\x85\xf6\x81\x2a\x75\xfb\xff\xcd\xec\xc3\x7c\x26\x44\x92\x51\xf2\x34\x18\x62\x2f\xc0\x7f\x94\x64\x0e\xf2\x53\x6d\xad\xb1\x1b\x18\x8b\x56\x6e\x1a\x25\xd9\x1a\x18\x5a\x68\x9b\xe2\x6c\x8b\xb2\xb1\x5c\xa9\x94\xb6\xca\xd6\x79\x8e\xc9\xd5\xf1\x18\x87\x03\xf6\x1d\xe5\xea\x78\x02\x79\xdf\x62\x3c\xac\x0b\x4c\xf3\x41\xe7\x39\xa5\x23\xcc\xbe\x9b\xc0\xca\x48\x5e\x82\x38\xc4\xf8\x12\x2f\xbd\xfa\x37\x6f\x45\x71\x7f\x3d\x9f\xcf\xfe\xeb\x6a\xb7\xd6\x73\xf1\x22\x84\x59\xe3\xe1\x01\xd1\x18\xd3\x29\x64\xdb\xa2\xc4\xe3\xe3\x25\xbb\x78\x41\xb6\x6b\xb4\x79\x15\x6b\xd3\x9c\xe2\x33\x6f\x6d\x6d\x2a\x1f\xa0\xab\x4d\x5d\x90\xe5\x60\xcd\x5b\x68\xd7\x19\xc8\x87\x9c\x02\xac\x2e\x08\xcf\x86\x07\xb9\xbe\xfd\x7a\xfd\x6d\x8e\x15\x75\x3a\xe7\x10\xdf\x6c\xee\x4e\x9b\x8a\x3c\x8c\xce\x9f\xf5\xce\xf7\xc4\x53\x34\x2b\xe1\xd9\xde\xf8\x1e\xeb\xca\x15\x88\x26\xad\x52\x97\xf0\x99\xab\xf3\xb4\x61\x96\xda\x7b\x3e\x9a\xb1\xc1\xb5\x08\x5d\x96\xaf\x5b\x15\x1c\x2f\x19\xe1\xa7\x32\xfa\x57\xbe\x7f\x8a\xf6\xaf\xe1\x51\x74\xce\x9b\xfa\xad\x30\x66\xa1\x55\xaa\xda\x2e\x7f\x02\xa7\xb2\x3d\x52\xf4\xea\xe2\x01\xa4\x10\xae\x0e\x65\x1d\xa6\xd1\x20\xfa\xc5\x3e\x14\xa2\xd4\xc9\x13\x85\x65\xee\x3c\xd3\x07\xed\x39\xa2\xce\x8f\x03\x42\x85\x78\x61\x63\x34\xff\x07\x34\x53\x9c\x59\xc4\x5e\x8d\x4e\xb0\x18\x8c\x4e\x16\xc3\xbf\xd1\xe5\xa3\xc9\x1f\x9d\xa8\xc5\x58\x95\xf1\x50\xe8\xed\x66\x99\xeb\x40\x36\xd9\xfd\x09\x94\xbf\x59\x98\x82\xb9\xaa\xa3\x2f\x54\x13\xbf\x63\x05\x9f\x8e\x2f\xb7\x4c\x75\xd0\xd3\x78\x2f\x7b\xbd\x2f\x4b\xe2\x9f\x90\x0d\x7a\x43\xf2\x42\xc6\x51\x4f\x8a\xe5\x29\x64\xaf\xa3\x65\xe1\x5b\x4b\xef\x29\x96\x2f\xb1\xe8\xda\xec\x95\xf8\x11\x00\x00\xff\xff\x84\x27\x4a\xf0\xe8\x03\x00\x00") -func testingTestletsPingBytes() ([]byte, error) { +func testingBashtestletsNotatallpingBytes() ([]byte, error) { return bindataRead( - _testingTestletsPing, - "testing/testlets/ping", + _testingBashtestletsNotatallping, + "testing/bashtestlets/notatallping", ) } -func testingTestletsPing() (*asset, error) { - bytes, err := testingTestletsPingBytes() +func testingBashtestletsNotatallping() (*asset, error) { + bytes, err := testingBashtestletsNotatallpingBytes() if err != nil { return nil, err } - info := bindataFileInfo{name: "testing/testlets/ping", size: 1000, mode: os.FileMode(493), modTime: time.Unix(1462644962, 0)} + info := bindataFileInfo{name: "testing/bashtestlets/notatallping", size: 1000, mode: os.FileMode(493), modTime: time.Unix(1463727404, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -147,7 +147,7 @@ func factsCollectorsGet_addresses() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "facts/collectors/get_addresses", size: 421, mode: os.FileMode(493), modTime: time.Unix(1462644938, 0)} + info := bindataFileInfo{name: "facts/collectors/get_addresses", size: 421, mode: os.FileMode(493), modTime: time.Unix(1463727404, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -167,7 +167,7 @@ func factsCollectorsGet_hostname() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "facts/collectors/get_hostname", size: 227, mode: os.FileMode(493), modTime: time.Unix(1462644938, 0)} + info := bindataFileInfo{name: "facts/collectors/get_hostname", size: 227, mode: os.FileMode(493), modTime: time.Unix(1463727404, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -224,9 +224,9 @@ func AssetNames() []string { // _bindata is a table, holding each asset generator, mapped to its name. var _bindata = map[string]func() (*asset, error){ - "testing/testlets/http": testingTestletsHttp, - "testing/testlets/iperf": testingTestletsIperf, - "testing/testlets/ping": testingTestletsPing, + "testing/bashtestlets/http": testingBashtestletsHttp, + "testing/bashtestlets/iperf": testingBashtestletsIperf, + "testing/bashtestlets/notatallping": testingBashtestletsNotatallping, "facts/collectors/get_addresses": factsCollectorsGet_addresses, "facts/collectors/get_hostname": factsCollectorsGet_hostname, } @@ -278,10 +278,10 @@ var _bintree = &bintree{nil, map[string]*bintree{ }}, }}, "testing": &bintree{nil, map[string]*bintree{ - "testlets": &bintree{nil, map[string]*bintree{ - "http": &bintree{testingTestletsHttp, map[string]*bintree{}}, - "iperf": &bintree{testingTestletsIperf, map[string]*bintree{}}, - "ping": &bintree{testingTestletsPing, map[string]*bintree{}}, + "bashtestlets": &bintree{nil, map[string]*bintree{ + "http": &bintree{testingBashtestletsHttp, map[string]*bintree{}}, + "iperf": &bintree{testingBashtestletsIperf, map[string]*bintree{}}, + "notatallping": &bintree{testingBashtestletsNotatallping, map[string]*bintree{}}, }}, }}, }} diff --git a/cmd/todd-agent/main.go b/cmd/todd-agent/main.go index 30950d9..c70e208 100644 --- a/cmd/todd-agent/main.go +++ b/cmd/todd-agent/main.go @@ -25,7 +25,7 @@ import ( "github.com/Mierdin/todd/hostresources" // Testlet imports (importing these packages registers the testlets) - _ "github.com/Mierdin/todd/agent/testing/testlets/ping" + _ "github.com/Mierdin/todd/agent/testing/downloaded_testlets" ) // Command-line Arguments diff --git a/cmd/todd-server/assets.go b/cmd/todd-server/assets.go index fc649c1..6ff9695 100644 --- a/cmd/todd-server/assets.go +++ b/cmd/todd-server/assets.go @@ -32,7 +32,7 @@ func serveAssets(cfg config.Config) map[string]map[string]string { // Initialize asset map assetMap := map[string]map[string]string{ "factcollectors": hashAssets("facts/collectors"), - "testlets": hashAssets("testing/testlets"), + "testlets": hashAssets("testing/bashtestlets"), } fmt.Println(assetMap) diff --git a/docs/nativetests.rst b/docs/nativetests.rst index e6c04a3..10ec9d2 100644 --- a/docs/nativetests.rst +++ b/docs/nativetests.rst @@ -3,4 +3,18 @@ Native Tests Need to talk about the native tests you've built in, and turn the "testlets" doc into more of a "so you want to build your own, eh?" -Also need to figure out if you want to refer to both native and non-native as "testlets", or maybe reserve that for non-native \ No newline at end of file +Also need to figure out if you want to refer to both native and non-native as "testlets", or maybe reserve that for non-native + + + +Need a design guide outlining some requirements for native testlets: + +* Testlets must honor the "kill" channel passed to the RunTestlet function. If a "true" value is passed into that channel, the testlet must quit immediately. + +* Need to put some specifics together regarding testlets that provide some kind of "server" functionality, kind of like what you've done for custom testlets + +* How do args work in native testlets? It's a bit awkward to continue to use command-line style args in a native testlet but might be necessary to preserve consistency for the user. + +* How to handle vendoring? If a testlet uses a library to run the tests, should the library get vendored with the testlet's repository, or within ToDD proper? Probably the former, but how is it used in that case? Just need to have a strategy here. (You probably vendored such libs in todd proper in order to develop them, so make sure you remove them once they're not needed) + +* How are errors returned from the testlet logic? If a testlet returns an error, how is this handled? \ No newline at end of file diff --git a/scripts/buildtestlets.sh b/scripts/buildtestlets.sh new file mode 100755 index 0000000..2c47c0f --- /dev/null +++ b/scripts/buildtestlets.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +# Copyright 2016 Matt Oswalt. Use or modification of this +# source code is governed by the license provided here: +# https://github.com/mierdin/todd/blob/master/LICENSE + +# This script downloads ToDD testlets prior to compile + +set -e +set -u +set -o pipefail + + +testlets=( + 'https://github.com/Mierdin/todd-nativetestlet-ping.git' + ) + + +rm -rf testlettemp && mkdir testlettemp && cd testlettemp + +for i in "${testlets[@]}" +do + git clone $i +done + +cd .. + +rm -rf agent/testing/downloaded_testlets/ && mkdir agent/testing/downloaded_testlets + +for dir in ./testlettemp/*/ +do + dir=${dir%*/} + cp testlettemp/${dir##*/}/testlet/* agent/testing/downloaded_testlets + #echo ${dir##*/} +done + +rm -rf testlettemp + + + +# rebuild plugins: +# _debug "removing: ${plugin_dir:?}/*" +# rm -rf "${plugin_dir:?}/"* +# mkdir -p "${plugin_dir}" + +# _info "building plugins" +# find "${__proj_dir}/plugin/" -type d -iname "snap-*" -print0 | xargs -0 -n 1 -I{} "${__dir}/build_plugin.sh" {} + +#--------- + + + +# __dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# __proj_dir="$(dirname "$__dir")" + +# # shellcheck source=scripts/common.sh +# . "${__dir}/common.sh" + +# build_dir="${__proj_dir}/build" +# plugin_dir="${build_dir}/plugin" + +# plugin_src_path=$1 +# plugin_name=$(basename "${plugin_src_path}") +# go_build=(go build -a -ldflags "-w") + +# _debug "plugin source: ${plugin_src_path}" +# _info "building ${plugin_name}" + +# (cd "${plugin_src_path}" && "${go_build[@]}" -o "${plugin_dir}/${plugin_name}" . || exit 1) \ No newline at end of file diff --git a/scripts/start-containers.sh b/scripts/start-containers.sh index 92891b6..bf76115 100755 --- a/scripts/start-containers.sh +++ b/scripts/start-containers.sh @@ -2,7 +2,7 @@ # Copyright 2016 Matt Oswalt. Use or modification of this # source code is governed by the license provided here: -# https://github.com/mierdin/todd:$branch/blob/master/LICENSE +# https://github.com/mierdin/todd/blob/master/LICENSE # This script is designed to manage containers for ToDD. This could be start the basic infrastructure for ToDD like the etcd and rabbitmq containers, # or you could run with the "integration" arg, and run integration tests as well. From 7ac7dca3bae018ddbfc8bfba7942d1313200c5b5 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Sun, 14 Aug 2016 22:56:40 -0700 Subject: [PATCH 036/120] Updating docs Signed-off-by: Matt Oswalt --- docs/customtestlets.rst | 71 +++++++++++++++++++++++++++++++++++ docs/dsl/group-datacenter.yml | 2 +- docs/index.rst | 1 + docs/testlets.rst | 67 +++++++-------------------------- etc/agent.cfg | 2 +- 5 files changed, 87 insertions(+), 56 deletions(-) create mode 100644 docs/customtestlets.rst diff --git a/docs/customtestlets.rst b/docs/customtestlets.rst new file mode 100644 index 0000000..a7023a0 --- /dev/null +++ b/docs/customtestlets.rst @@ -0,0 +1,71 @@ +Custom Testlets +================================ + +ToDD was originally built with no testlets built-in to the agent. All tests were performed using external executable files (i.e. scripts, binaries) that accept a standard set of input, run a test application, and provide a standard set of output containing metrics from that test. Though ToDD has since adopted a number of testlets to be built-in to the agent for simplicity, this functionality still remains, so you can extend ToDD to run whatever types of tests you wish. + +This allows the user to use any testing application (provided it is available on the system on which the ToDD agent is running, and specify which agents run this application. All of the complicated stuff with respect to sending arguments to the underlying testing application as well as parsing the output, is performed inside the testlet. + +.. image:: images/testlet.png + +The testlet is actually run by the ToDD agent, so if there are 3 agents participating in a test, then 3 testlets are running. All logic that performs the test should be contained within the + +Testrun Definition +------------------ + +When you want to run a certain testlet, you refer to it by name. There are a number of testlets built-in to ToDD and are therefore reserved: + +* http +* bandwidth +* ping + +You can, of course, build your own testlet (provided it follows the standard defined on this page) and refer to it by it's filename. + +Check Mode +---------- +Each testlet must support a "check mode". This is a way of running a testlet that allows the ToDD agent to know whether or not a test can be performed, without actually running the test. + +For instance, when the ToDD agent runs the "ping" testlet in check mode, it would invoke it like this: + +.. code-block:: text + + ./testletname check + +That said, the ToDD Server will distribute testrun instructions to the agents in two phases: + +* Install - run the referenced testlet in check mode, and record all params in local agent cache +* Execute - run the installed testrun instruction + +Input +----- +There is little to no similarity between various testing applications with respect to the parameters required by those applications. However, in order to simplify things for the ToDD agent, the testlet - due to it's place as a "wrapper" for a testing application - standardizes this input so the ToDD agent can invoke any testlet in a consistent manner + +.. code-block:: text + + ./testletname < target > < args > + +The ToDD agent will execute the testlet as it exists on the system, and will pass a few arguments to it (meaning the testlet must support and honor these arguments): + +* "target" - this is always the first parameter - represents the IP address or FQDN of the target for this test instance. +* "args" - any arguments required by the underlying application. These should be passed to that application via the testlet + +Output +------ +The output for every testlet is a single-level JSON object, which contains key-value pairs for the metrics gathered for that testlet. + +Since the ToDD agent is responsible for executing a testlet, it is also watching stdout for the testlet to provide this JSON object. This is one of the things that make testlets a very flexible method of performing tests - since it only needs to output these metrics as JSON to stdout, the testlet can be written in any language, as long as they support the arguments described in the "Input" section. + +A sample JSON object that the "ping" testlet will provide is shown below: + +.. code-block:: text + + { + "avg_latency_ms": "27.007", + "packet_loss_percentage": "0" + } + +This specific output covers the metrics for a single testlet run, which means that this is relevant to only a single target, run by a single ToDD agent. The ToDD agent will receive this output once for each target in the testrun, and submit this up to the ToDD server for collection. + +.. NOTE:: + The ToDD Server will also aggregate each agent's report to a single metric document for the entire testrun, so that it's easy to see the metrics for each source-to-target relationship for a testrun. + +The ToDD agent does not have an opinion on the values contained in the keys or values for this JSON object, or how many k/v pairs there are - only that it is valid JSON, and is a single level (no nested objects, lists, etc). \ No newline at end of file diff --git a/docs/dsl/group-datacenter.yml b/docs/dsl/group-datacenter.yml index 1ee95b3..5a2c541 100644 --- a/docs/dsl/group-datacenter.yml +++ b/docs/dsl/group-datacenter.yml @@ -4,4 +4,4 @@ label: datacenter spec: group: datacenter matches: - - within_subnet: "172.17.0.0/16" \ No newline at end of file + - within_subnet: "172.17.0.0/16" diff --git a/docs/index.rst b/docs/index.rst index 1a17e95..e117dca 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -18,6 +18,7 @@ ToDD Documentation objects.rst resources.rst testlets.rst + customtestlets.rst This is ToDD's documentation. The docs are a work in progress, but check back soon! I'll be updating this heavily over the next few days and weeks. diff --git a/docs/testlets.rst b/docs/testlets.rst index 06afe4d..740690b 100644 --- a/docs/testlets.rst +++ b/docs/testlets.rst @@ -1,71 +1,30 @@ -ToDD Testlets +Testlets ================================ -Tests in ToDD are powered by something called "testlets". These are executable files (i.e. scripts, binaries) that accept a standard set of input, run a test application, and provide a standard set of output containing metrics from that test. +Testing applications are referred to "testlets" in ToDD. This is a handy way of referring to "whatever is actually doing the work". ToDD simply orchestrates this work. -This allows the user to simply "use" this application, and specify which agents run this application. All of the complicated stuff with respect to sending arguments to the underlying testing application as well as parsing the output, is performed inside the testlet. - -.. image:: images/testlet.png - -The testlet is actually run by the ToDD agent, so if there are 3 agents participating in a test, then 3 testlets are running. All logic that performs the test should be contained within the - -Testrun Definition ------------------- - -When you want to run a certain testlet, you refer to it by name. There are a number of testlets built in to ToDD and are therefore reserved: +There are a number of testlets built-in to the ToDD agent and are usable simply by installing the agent on a system: * http * bandwidth * ping -If you don't want to use any of the built-in testlets, you can, of course, build your own testlet (provided it follows the standard defined on this page) and refer to it by it's filename. - -Check Mode ----------- -Each testlet must support a "check mode". This is a way of running a testlet that allows the ToDD agent to know whether or not a test can be performed, without actually running the test. - -For instance, when the ToDD agent runs the "ping" testlet in check mode, it would invoke it like this: - -.. code-block:: text - - ./testletname check - -That said, the ToDD Server will distribute testrun instructions to the agents in two phases: - -* Install - run the referenced testlet in check mode, and record all params in local agent cache -* Execute - run the installed testrun instruction - -Input ------ -There is little to no similarity between various testing applications with respect to the parameters required by those applications. However, in order to simplify things for the ToDD agent, the testlet - due to it's place as a "wrapper" for a testing application - standardizes this input so the ToDD agent can invoke any testlet in a consistent manner - -.. code-block:: text - - ./testletname < target > < args > - -The ToDD agent will execute the testlet as it exists on the system, and will pass a few arguments to it (meaning the testlet must support and honor these arguments): +However, please see "Custom Testlets", and you'll find it's quite easy to build your own testlets and run them with ToDD. This extensibility was a core design principle of ToDD since the beginning of the project. -* "target" - this is always the first parameter - represents the IP address or FQDN of the target for this test instance. -* "args" - any arguments required by the underlying application. These should be passed to that application via the testlet -Output ------- -The output for every testlet is a single-level JSON object, which contains key-value pairs for the metrics gathered for that testlet. +Native Testlet Design Principles +-------------------------------- -Since the ToDD agent is responsible for executing a testlet, it is also watching stdout for the testlet to provide this JSON object. This is one of the things that make testlets a very flexible method of performing tests - since it only needs to output these metrics as JSON to stdout, the testlet can be written in any language, as long as they support the arguments described in the "Input" section. +Need a design guide outlining some requirements for native testlets: -A sample JSON object that the "ping" testlet will provide is shown below: +* Testlets must honor the "kill" channel passed to the RunTestlet function. If a "true" value is passed into that channel, the testlet must quit immediately. -.. code-block:: text +* Need to put some specifics together regarding testlets that provide some kind of "server" functionality, kind of like what you've done for custom testlets - { - "avg_latency_ms": "27.007", - "packet_loss_percentage": "0" - } +* How do args work in native testlets? It's a bit awkward to continue to use command-line style args in a native testlet but might be necessary to preserve consistency for the user. -This specific output covers the metrics for a single testlet run, which means that this is relevant to only a single target, run by a single ToDD agent. The ToDD agent will receive this output once for each target in the testrun, and submit this up to the ToDD server for collection. +* How to handle vendoring? If a testlet uses a library to run the tests, should the library get vendored with the testlet's repository, or within ToDD proper? Probably the former, but how is it used in that case? Just need to have a strategy here. (You probably vendored such libs in todd proper in order to develop them, so make sure you remove them once they're not needed) -.. NOTE:: - The ToDD Server will also aggregate each agent's report to a single metric document for the entire testrun, so that it's easy to see the metrics for each source-to-target relationship for a testrun. +* How are errors returned from the testlet logic? If a testlet returns an error, how is this handled? -The ToDD agent does not have an opinion on the values contained in the keys or values for this JSON object, or how many k/v pairs there are - only that it is valid JSON, and is a single level (no nested objects, lists, etc). \ No newline at end of file +* How does development work? Do you clone the testlet repo next to the todd repo, kind of like devstack? \ No newline at end of file diff --git a/etc/agent.cfg b/etc/agent.cfg index cfdfbdf..cca07b0 100644 --- a/etc/agent.cfg +++ b/etc/agent.cfg @@ -6,6 +6,6 @@ Port = 5672 Plugin = rabbitmq [LocalResources] -DefaultInterface = eth0 +DefaultInterface = eth2 # IPAddrOverride = 192.168.99.100 # Normally, the DefaultInterface configuration option is used to get IP address. This overrides that in the event that it doesn't work OptDir = /opt/todd/agent From 02bd94e0c788bcdee5dcc79c14e12c9b8ddb29a7 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Fri, 19 Aug 2016 12:00:41 -0700 Subject: [PATCH 037/120] Updates to vagrant and make Signed-off-by: Matt Oswalt --- Makefile | 5 +++++ Vagrantfile | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d5e11f3..b0f6661 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,8 @@ build: docker build -t mierdin/todd -f Dockerfile . compile: + # Need to support some kind of mode that allows for development (i.e. not downloading the testlets live, but rather from a directory?) + # eval ./scripts/buildtestlets.sh && go install ./cmd/... ./scripts/buildtestlets.sh go install ./cmd/... @@ -42,3 +44,6 @@ configureenv: if ! [ "etc" -ef "/etc/todd" ]; then mkdir -p /etc/todd && cp -f ./etc/{agent,server}.cfg /etc/todd/; fi mkdir -p /opt/todd/{agent,server}/assets/{factcollectors,testlets} chmod -R 777 /opt/todd + + # If on Linux, enable ping testlet functionality + sudo sysctl -w net.ipv4.ping_group_range="0 12345" diff --git a/Vagrantfile b/Vagrantfile index 9317dee..a9efd37 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -6,7 +6,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.box = "trusty64" config.vm.box_url = "http://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-amd64-vagrant-disk1.box" - config.vm.synced_folder '.', '/home/vagrant/go/src/github.com/Mierdin/todd', nfs: true + config.vm.synced_folder '../../', '/home/vagrant/go/src/github.com', nfs: true config.vm.provider "virtualbox" do |v| From 0a229bd856e8bf7382c3ca9a17c13e2032c476e4 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Fri, 19 Aug 2016 12:03:45 -0700 Subject: [PATCH 038/120] Catch up Signed-off-by: Matt Oswalt --- cmd/todd-agent/main.go | 5 ++-- docs/dsl/branch-to-dc-bw.yml | 14 +++++++++++ docs/dsl/group-branch.yml | 7 ++++++ docs/dsl/group-uraj.yml | 7 ++++++ docs/nativetests.rst | 20 ---------------- docs/roadmap.rst | 17 +++++++++++++ docs/testlets.rst | 9 +++++++ scripts/buildtestlets.sh | 46 ++++++++++++++++++++++++++---------- 8 files changed, 91 insertions(+), 34 deletions(-) create mode 100644 docs/dsl/branch-to-dc-bw.yml create mode 100644 docs/dsl/group-branch.yml create mode 100644 docs/dsl/group-uraj.yml delete mode 100644 docs/nativetests.rst create mode 100644 docs/roadmap.rst diff --git a/cmd/todd-agent/main.go b/cmd/todd-agent/main.go index c70e208..880efc7 100644 --- a/cmd/todd-agent/main.go +++ b/cmd/todd-agent/main.go @@ -25,8 +25,9 @@ import ( "github.com/Mierdin/todd/hostresources" // Testlet imports (importing these packages registers the testlets) - _ "github.com/Mierdin/todd/agent/testing/downloaded_testlets" -) + // Need to make this dynamic if possible (at compile time of course) + // TODO Not necessary anymore + //_ "github.com/toddproject/todd-nativetestlet-ping/testlet" // Command-line Arguments var arg_config string diff --git a/docs/dsl/branch-to-dc-bw.yml b/docs/dsl/branch-to-dc-bw.yml new file mode 100644 index 0000000..b1683a4 --- /dev/null +++ b/docs/dsl/branch-to-dc-bw.yml @@ -0,0 +1,14 @@ +--- +# Example test file +type: testrun +label: branch-to-dc-bw +spec: + targettype: group + source: + name: branch-uraj + app: iperf + args: "-c {{ target }}" + target: + name: datacenter + app: iperf + args: "-s" diff --git a/docs/dsl/group-branch.yml b/docs/dsl/group-branch.yml new file mode 100644 index 0000000..b1d1e82 --- /dev/null +++ b/docs/dsl/group-branch.yml @@ -0,0 +1,7 @@ +--- +type: group +label: branch-uraj +spec: + group: branch-uraj + matches: + - hostname: "uraj" diff --git a/docs/dsl/group-uraj.yml b/docs/dsl/group-uraj.yml new file mode 100644 index 0000000..b1d1e82 --- /dev/null +++ b/docs/dsl/group-uraj.yml @@ -0,0 +1,7 @@ +--- +type: group +label: branch-uraj +spec: + group: branch-uraj + matches: + - hostname: "uraj" diff --git a/docs/nativetests.rst b/docs/nativetests.rst deleted file mode 100644 index 10ec9d2..0000000 --- a/docs/nativetests.rst +++ /dev/null @@ -1,20 +0,0 @@ -Native Tests -================================ - -Need to talk about the native tests you've built in, and turn the "testlets" doc into more of a "so you want to build your own, eh?" - -Also need to figure out if you want to refer to both native and non-native as "testlets", or maybe reserve that for non-native - - - -Need a design guide outlining some requirements for native testlets: - -* Testlets must honor the "kill" channel passed to the RunTestlet function. If a "true" value is passed into that channel, the testlet must quit immediately. - -* Need to put some specifics together regarding testlets that provide some kind of "server" functionality, kind of like what you've done for custom testlets - -* How do args work in native testlets? It's a bit awkward to continue to use command-line style args in a native testlet but might be necessary to preserve consistency for the user. - -* How to handle vendoring? If a testlet uses a library to run the tests, should the library get vendored with the testlet's repository, or within ToDD proper? Probably the former, but how is it used in that case? Just need to have a strategy here. (You probably vendored such libs in todd proper in order to develop them, so make sure you remove them once they're not needed) - -* How are errors returned from the testlet logic? If a testlet returns an error, how is this handled? \ No newline at end of file diff --git a/docs/roadmap.rst b/docs/roadmap.rst new file mode 100644 index 0000000..9c9be29 --- /dev/null +++ b/docs/roadmap.rst @@ -0,0 +1,17 @@ +Roadmap +================================ + +Goals for Alpha Release + +* Store group-to-group aggreagate test data as a graph (agent groups are the lowest addressable node in teh graph) +* Consider getting rid of YAML files. It should be easy to run tests using a one-liner (native testlets should have reasonable defaults) or they just run on their own in the background +* Simplify agent setup. Is there a way to provide configuration-less agent setup? +* Need to look at modifying ToDD to run tests in a hands-off way. Think about the inspiration from the vendors at NFD12. Need to rethink the execution of tests. You know you can store test results without server connectivity, but can you run tests without server connectivity? + +Goals for Beta Release + +* Goal 1 + +Goals for Full Release + +* Web Front-End? \ No newline at end of file diff --git a/docs/testlets.rst b/docs/testlets.rst index 740690b..092742d 100644 --- a/docs/testlets.rst +++ b/docs/testlets.rst @@ -8,6 +8,9 @@ There are a number of testlets built-in to the ToDD agent and are usable simply * http * bandwidth * ping +* portknock + +These have their own separate repositories and are distributed alongside ToDD proper. They are written in Go for a number of reasons. First, it makes it easy for the testlets to honor the testlet format by leveraging some common code in the ToDD repository. However, the testlets are still their own binary. In addition, it allows ToDD to execute tests consistently across platforms (The old model of using bash scripts meant the tests had to be run on a certain platform for which that testlet knew how to parse the output) However, please see "Custom Testlets", and you'll find it's quite easy to build your own testlets and run them with ToDD. This extensibility was a core design principle of ToDD since the beginning of the project. @@ -15,6 +18,12 @@ However, please see "Custom Testlets", and you'll find it's quite easy to build Native Testlet Design Principles -------------------------------- + +Need to talk about the native tests you've built in, and turn the "testlets" doc into more of a "so you want to build your own, eh?" + +Also need to figure out if you want to refer to both native and non-native as "testlets", or maybe reserve that for non-native + + Need a design guide outlining some requirements for native testlets: * Testlets must honor the "kill" channel passed to the RunTestlet function. If a "true" value is passed into that channel, the testlet must quit immediately. diff --git a/scripts/buildtestlets.sh b/scripts/buildtestlets.sh index 2c47c0f..0875fb1 100755 --- a/scripts/buildtestlets.sh +++ b/scripts/buildtestlets.sh @@ -12,29 +12,51 @@ set -o pipefail testlets=( - 'https://github.com/Mierdin/todd-nativetestlet-ping.git' + 'github.com/toddproject/todd-nativetestlet-ping' ) -rm -rf testlettemp && mkdir testlettemp && cd testlettemp +#rm -rf testlettemp && mkdir testlettemp && cd testlettemp for i in "${testlets[@]}" do - git clone $i + #echo "Installing $i" + # git clone $i --quiet + go get -d -u $i/... done -cd .. +# cd .. + +# rm -rf agent/testing/downloaded_testlets/ && mkdir agent/testing/downloaded_testlets + +# for dir in ./testlettemp/*/ +# do +# dir=${dir%*/} +# cp testlettemp/${dir##*/}/testlet/* agent/testing/downloaded_testlets +# #echo ${dir##*/} + + + +# testletdir="$(pwd)/$dir" +# #echo $testletdir + +# ln -s $testletdir/vendor/ $testletdir/vendor/src + +# # echo ./testlettemp/todd-nativetestlet-ping + +# # Append this vendor directory to GOPATH +# # TODO need to do some cleanup somewhere to remove this +# if [[ ":$GOPATH:" != *":$testletdir/vendor:"* ]]; then +# echo "export GOPATH=$GOPATH:$testletdir/vendor" +# fi + + +# done + -rm -rf agent/testing/downloaded_testlets/ && mkdir agent/testing/downloaded_testlets -for dir in ./testlettemp/*/ -do - dir=${dir%*/} - cp testlettemp/${dir##*/}/testlet/* agent/testing/downloaded_testlets - #echo ${dir##*/} -done -rm -rf testlettemp +# rm -rf testlettemp From 4929bec6d46d5586b945ef8b1db7c2031d039d0d Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Fri, 19 Aug 2016 14:52:22 -0700 Subject: [PATCH 039/120] Got native testlets working Signed-off-by: Matt Oswalt --- agent/tasks/executetestrun.go | 176 +++++++++++++++++------------ agent/tasks/installtestrun.go | 55 ++++----- agent/testing/testlets/testlets.go | 19 ++-- cmd/todd-agent/main.go | 2 +- 4 files changed, 143 insertions(+), 109 deletions(-) diff --git a/agent/tasks/executetestrun.go b/agent/tasks/executetestrun.go index b9210de..182a14a 100644 --- a/agent/tasks/executetestrun.go +++ b/agent/tasks/executetestrun.go @@ -69,94 +69,124 @@ func (ett ExecuteTestRunTask) Run() error { // Specify size of wait group wg.Add(len(tr.Targets)) + //native := false + + // for old, newname := range testlets.nativeTestlets { + // if tr.Testlet == old { + // tr.Testlet = newname + // native = true + // break + // } + // } + + // log.Error(tr.Testlet) + // log.Error(testlets.IsNativeTestlet(tr.Testlet)) + + var testlet_path string + //if testlets.IsNativeTestlet(tr.Testlet) { + isNative, newTestletName := testlets.IsNativeTestlet(tr.Testlet) + log.Error("POOP") + log.Error(isNative) + log.Error(newTestletName) + if isNative { + + log.Error("POOP2") + + tr.Testlet = newTestletName + + log.Error(tr.Testlet) + + // Generate path to testlet and make sure it exists. + testlet_path = fmt.Sprintf("%s", tr.Testlet) + // if _, err := os.Stat(testlet_path); os.IsNotExist(err) { + // log.Error(err) + // log.Errorf("Testlet %s does not exist on this agent", tr.Testlet) + // return errors.New("Error executing testrun - testlet doesn't exist on this agent.") + // } + } else { + // Generate path to testlet and make sure it exists. + testlet_path = fmt.Sprintf("%s/assets/testlets/%s", ett.Config.LocalResources.OptDir, tr.Testlet) + if _, err := os.Stat(testlet_path); os.IsNotExist(err) { + log.Errorf("Testlet %s does not exist on this agent", tr.Testlet) + return errors.New("Error executing testrun - testlet doesn't exist on this agent.") + } + } + // Execute testlets against all targets asynchronously for i := range tr.Targets { thisTarget := tr.Targets[i] - if testlets.IsNativeTestlet(tr.Testlet) { - go func() { + go func() { - // TODO(mierdin): Something worried me here (can't remember what) regarding - // if only some agents were running native testlets, does this wait group methodology work? - // Sorry it's not clear, I have had a bit too much wine. - defer wg.Done() + defer wg.Done() - nativeTestlet, err := testlets.NewTestlet(tr.Testlet) - if err != nil { - //TODO(mierdin) do something - } - - metrics, err := nativeTestlet.Run("8.8.8.8", []string{"-c 10", "-s"}, ett.TimeLimit) - //log.Error(nativeTestlet.RunFunction) - if err != nil { - log.Errorf("Testlet completed with error '%s'", err) - gatheredData[thisTarget] = "error" - } + log.Debugf("Full testlet command and args: '%s %s %s'", tr.Testlet, thisTarget, tr.Args) + cmd := exec.Command(tr.Testlet, thisTarget, tr.Args) - // The metrics infrastructure requires that we collect metrics as a JSON string - // (which is a result of building non-native testlets in early versions of ToDD) - // So let's convert, and add to gatheredData - metrics_json, err := json.Marshal(metrics) - if err != nil { - //TODO(mierdin) do something - } - gatheredData[thisTarget] = string(metrics_json) + // Stdout buffer + cmdOutput := &bytes.Buffer{} + // Attach buffer to command + cmd.Stdout = cmdOutput - }() - } else { - // Generate path to testlet and make sure it exists. - testlet_path := fmt.Sprintf("%s/assets/testlets/%s", ett.Config.LocalResources.OptDir, tr.Testlet) - if _, err := os.Stat(testlet_path); os.IsNotExist(err) { - log.Errorf("Testlet %s does not exist on this agent", tr.Testlet) - return errors.New("Error executing testrun - testlet doesn't exist on this agent.") - } + // Execute collector + cmd.Start() + // TODO(mierdin): Does this need to be a buffered channel? + done := make(chan error, 1) go func() { + done <- cmd.Wait() + }() - defer wg.Done() - - log.Debugf("Full testlet command and args: '%s %s %s'", testlet_path, thisTarget, tr.Args) - cmd := exec.Command(testlet_path, thisTarget, tr.Args) - - // Stdout buffer - cmdOutput := &bytes.Buffer{} - // Attach buffer to command - cmd.Stdout = cmdOutput - - // Execute collector - cmd.Start() - - // TODO(mierdin): Does this need to be a buffered channel? - done := make(chan error, 1) - go func() { - done <- cmd.Wait() - }() - - // This select statement will block until one of these two conditions are met: - // - The testlet finishes, in which case the channel "done" will be receive a value - // - The configured time limit is exceeded (expected for testlets running in server mode) - select { - case <-time.After(time.Duration(ett.TimeLimit) * time.Second): - if err := cmd.Process.Kill(); err != nil { - log.Errorf("Failed to kill %s after timeout: %s", testlet_path, err) - } else { - log.Debug("Successfully killed ", testlet_path) - } - case err := <-done: - if err != nil { - log.Errorf("Testlet %s completed with error '%s'", testlet_path, err) - gatheredData[thisTarget] = "error" - } else { - log.Debugf("Testlet %s completed without error", testlet_path) - } + // This select statement will block until one of these two conditions are met: + // - The testlet finishes, in which case the channel "done" will be receive a value + // - The configured time limit is exceeded (expected for testlets running in server mode) + select { + case <-time.After(time.Duration(ett.TimeLimit) * time.Second): + if err := cmd.Process.Kill(); err != nil { + log.Errorf("Failed to kill %s after timeout: %s", testlet_path, err) + } else { + log.Debug("Successfully killed ", testlet_path) } + case err := <-done: + if err != nil { + log.Errorf("Testlet %s completed with error '%s'", testlet_path, err) + gatheredData[thisTarget] = "error" + } else { + log.Debugf("Testlet %s completed without error", testlet_path) + } + } - // Record test data - gatheredData[thisTarget] = string(cmdOutput.Bytes()) + // Record test data + gatheredData[thisTarget] = string(cmdOutput.Bytes()) + // // TODO(mierdin): Something worried me here (can't remember what) regarding + // // if only some agents were running native testlets, does this wait group methodology work? + // // Sorry it's not clear, I have had a bit too much wine. + // defer wg.Done() + + // nativeTestlet, err := testlets.NewTestlet(tr.Testlet) + // if err != nil { + // //TODO(mierdin) do something + // } + + // metrics, err := nativeTestlet.Run("8.8.8.8", []string{"-c 10", "-s"}, ett.TimeLimit) + // //log.Error(nativeTestlet.RunFunction) + // if err != nil { + // log.Errorf("Testlet completed with error '%s'", err) + // gatheredData[thisTarget] = "error" + // } + + // // The metrics infrastructure requires that we collect metrics as a JSON string + // // (which is a result of building non-native testlets in early versions of ToDD) + // // So let's convert, and add to gatheredData + // metrics_json, err := json.Marshal(metrics) + // if err != nil { + // //TODO(mierdin) do something + // } + // gatheredData[thisTarget] = string(metrics_json) + + }() - }() - } } wg.Wait() diff --git a/agent/tasks/installtestrun.go b/agent/tasks/installtestrun.go index 131df4d..96bb662 100644 --- a/agent/tasks/installtestrun.go +++ b/agent/tasks/installtestrun.go @@ -43,48 +43,49 @@ func (itt InstallTestRunTask) Run() error { return errors.New("Testlet parameter for this testrun is null") } - // Determine if this is a valid native testlet - _, err := testlets.NewTestlet(itt.Tr.Testlet) + var testlet_path string + // Determine if this is a valid native testlet + //_, err := testlets.NewTestlet(itt.Tr.Testlet) + isNative, newName := testlets.IsNativeTestlet(itt.Tr.Testlet) // Not a native testlet - attempt to run check mode on testlet in filesystem - if err != nil { + if isNative { + // Nothing to do, as we're using a native testlet + log.Infof("%s is a native testlet - installing testrun.", itt.Tr.Testlet) + //itt.Tr.Testlet = newName + testlet_path = newName + } else { // Generate path to testlet and make sure it exists. - testlet_path := fmt.Sprintf("%s/assets/testlets/%s", itt.Config.LocalResources.OptDir, itt.Tr.Testlet) + testlet_path = fmt.Sprintf("%s/assets/testlets/%s", itt.Config.LocalResources.OptDir, itt.Tr.Testlet) if _, err := os.Stat(testlet_path); os.IsNotExist(err) { log.Errorf("Testlet %s does not exist on this agent", itt.Tr.Testlet) return errors.New("Error installing testrun - testlet doesn't exist on this agent.") } + } - // Run the testlet in check mode to verify that everything is okay to run this test - log.Debug("Running testlet in check mode: ", testlet_path) - cmd := exec.Command(testlet_path, "check") - - // Stdout buffer - cmdOutput := &bytes.Buffer{} - // Attach buffer to command - cmd.Stdout = cmdOutput - // Execute collector - cmd.Run() - - // This is probably the best cross-platform way to see if check mode passed. - if strings.Contains(string(cmdOutput.Bytes()), "Check mode PASSED") { - log.Debugf("Check mode for %s passed", testlet_path) - } else { - log.Error("Testlet returned an error during check mode: ", string(cmdOutput.Bytes())) - return errors.New("Testlet returned an error during check mode") - } - - } else { + // Run the testlet in check mode to verify that everything is okay to run this test + log.Debug("Running testlet in check mode: ", testlet_path) + cmd := exec.Command(testlet_path, "check") - // Nothing to do, as we're using a native testlet - log.Infof("%s is a native testlet - installing testrun.", itt.Tr.Testlet) + // Stdout buffer + cmdOutput := &bytes.Buffer{} + // Attach buffer to command + cmd.Stdout = cmdOutput + // Execute collector + cmd.Run() + // This is probably the best cross-platform way to see if check mode passed. + if strings.Contains(string(cmdOutput.Bytes()), "Check mode PASSED") { + log.Debugf("Check mode for %s passed", testlet_path) + } else { + log.Error("Testlet returned an error during check mode: ", string(cmdOutput.Bytes())) + return errors.New("Testlet returned an error during check mode") } // Insert testrun into agent cache var ac = cache.NewAgentCache(itt.Config) - err = ac.InsertTestRun(itt.Tr) + err := ac.InsertTestRun(itt.Tr) if err != nil { log.Error(err) return errors.New("Problem installing test run into agent cache") diff --git a/agent/testing/testlets/testlets.go b/agent/testing/testlets/testlets.go index 4458892..7bcafe1 100644 --- a/agent/testing/testlets/testlets.go +++ b/agent/testing/testlets/testlets.go @@ -12,10 +12,13 @@ import ( ) var ( - testletsMu sync.RWMutex - testlets = make(map[string]Testlet) - done = make(chan error) // Used from within the goroutine to inform the infrastructure it has finished - kill = make(chan bool) // Used from outside the goroutine to inform the goroutine to stop + testletsMu sync.RWMutex + testlets = make(map[string]Testlet) + done = make(chan error) // Used from within the goroutine to inform the infrastructure it has finished + kill = make(chan bool) // Used from outside the goroutine to inform the goroutine to stop + nativeTestlets = map[string]string{ + "ping": "toddping", + } ) // Testlet defines what a testlet should look like if built in native @@ -107,11 +110,11 @@ func (b BaseTestlet) Kill() error { // IsNativeTestlet polls the list of registered native testlets, and returns // true if the referenced name exists -func IsNativeTestlet(name string) bool { - if _, ok := testlets[name]; ok { - return true +func IsNativeTestlet(name string) (bool, string) { + if _, ok := nativeTestlets[name]; ok { + return true, nativeTestlets[name] } else { - return false + return false, "" } } diff --git a/cmd/todd-agent/main.go b/cmd/todd-agent/main.go index 880efc7..a7f527e 100644 --- a/cmd/todd-agent/main.go +++ b/cmd/todd-agent/main.go @@ -23,11 +23,11 @@ import ( "github.com/Mierdin/todd/comms" "github.com/Mierdin/todd/config" "github.com/Mierdin/todd/hostresources" - // Testlet imports (importing these packages registers the testlets) // Need to make this dynamic if possible (at compile time of course) // TODO Not necessary anymore //_ "github.com/toddproject/todd-nativetestlet-ping/testlet" +) // Command-line Arguments var arg_config string From 19f2de95e444ff0df2805095852d22a94dddc545 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Mon, 22 Aug 2016 23:39:44 -0700 Subject: [PATCH 040/120] Catch up Signed-off-by: Matt Oswalt --- Makefile | 9 ++- agent/tasks/executetestrun.go | 79 ++++----------------- agent/tasks/installtestrun.go | 11 ++- agent/testing/bashtestlets/notatallping | 32 --------- agent/testing/testlets/testlets.go | 34 +++++++-- scripts/buildtestlets.sh | 91 ------------------------- scripts/gettestlets.sh | 23 +++++++ 7 files changed, 77 insertions(+), 202 deletions(-) delete mode 100755 agent/testing/bashtestlets/notatallping delete mode 100755 scripts/buildtestlets.sh create mode 100755 scripts/gettestlets.sh diff --git a/Makefile b/Makefile index b0f6661..adb170b 100644 --- a/Makefile +++ b/Makefile @@ -11,9 +11,12 @@ build: docker build -t mierdin/todd -f Dockerfile . compile: - # Need to support some kind of mode that allows for development (i.e. not downloading the testlets live, but rather from a directory?) - # eval ./scripts/buildtestlets.sh && go install ./cmd/... - ./scripts/buildtestlets.sh + # TODO(mierdin): Need to support some kind of mode that allows for development (i.e. not downloading the testlets live, but rather from a directory?) + + # Installing testlets + ./scripts/gettestlets.sh + + # Installing ToDD go install ./cmd/... install: configureenv diff --git a/agent/tasks/executetestrun.go b/agent/tasks/executetestrun.go index 182a14a..de1ebbc 100644 --- a/agent/tasks/executetestrun.go +++ b/agent/tasks/executetestrun.go @@ -52,8 +52,9 @@ func (ett ExecuteTestRunTask) Run() error { gatheredData = map[string]string{} // Waiting three seconds to ensure all the agents have their tasks before we potentially hammer the network - // TODO(mierdin): This is a bit of a copout. I would like to do something a little more robust than simply waiting - // for a few seconds in the future. + // TODO(mierdin): This is a temporary measure - in the future, testruns will be executed via time schedule, + // making not only this sleep, but also the entire task unnecessary. Testruns will simply be installed, and + // executed when the time is right. time.Sleep(3000 * time.Millisecond) // Retrieve test from cache by UUID @@ -66,52 +67,29 @@ func (ett ExecuteTestRunTask) Run() error { log.Debugf("IMMA FIRIN MAH LAZER (for test %s) ", ett.TestUuid) - // Specify size of wait group + // Specify size of wait group equal to number of targets wg.Add(len(tr.Targets)) - //native := false - - // for old, newname := range testlets.nativeTestlets { - // if tr.Testlet == old { - // tr.Testlet = newname - // native = true - // break - // } - // } - - // log.Error(tr.Testlet) - // log.Error(testlets.IsNativeTestlet(tr.Testlet)) - var testlet_path string - //if testlets.IsNativeTestlet(tr.Testlet) { isNative, newTestletName := testlets.IsNativeTestlet(tr.Testlet) - log.Error("POOP") - log.Error(isNative) - log.Error(newTestletName) - if isNative { - - log.Error("POOP2") - - tr.Testlet = newTestletName - log.Error(tr.Testlet) - - // Generate path to testlet and make sure it exists. - testlet_path = fmt.Sprintf("%s", tr.Testlet) - // if _, err := os.Stat(testlet_path); os.IsNotExist(err) { - // log.Error(err) - // log.Errorf("Testlet %s does not exist on this agent", tr.Testlet) - // return errors.New("Error executing testrun - testlet doesn't exist on this agent.") - // } + // If we're running a native testlet, we want testlet_path to simply be the testlet name + // (since it is a requirement that the native-Go testlets are in the PATH) + // If the testlet is not native, we can get the full path. + if isNative { + testlet_path = newTestletName } else { // Generate path to testlet and make sure it exists. testlet_path = fmt.Sprintf("%s/assets/testlets/%s", ett.Config.LocalResources.OptDir, tr.Testlet) if _, err := os.Stat(testlet_path); os.IsNotExist(err) { - log.Errorf("Testlet %s does not exist on this agent", tr.Testlet) + log.Errorf("Testlet %s does not exist on this agent", testlet_path) return errors.New("Error executing testrun - testlet doesn't exist on this agent.") } } + // TODO(mierdin): What about testlets running as servers (i.e. 'iperf -s')? Are we spinning up len(tr.Targets) + // number of those? + // Execute testlets against all targets asynchronously for i := range tr.Targets { @@ -121,8 +99,8 @@ func (ett ExecuteTestRunTask) Run() error { defer wg.Done() - log.Debugf("Full testlet command and args: '%s %s %s'", tr.Testlet, thisTarget, tr.Args) - cmd := exec.Command(tr.Testlet, thisTarget, tr.Args) + log.Debugf("Full testlet command and args: '%s %s %s'", testlet_path, thisTarget, tr.Args) + cmd := exec.Command(testlet_path, thisTarget, tr.Args) // Stdout buffer cmdOutput := &bytes.Buffer{} @@ -159,34 +137,7 @@ func (ett ExecuteTestRunTask) Run() error { // Record test data gatheredData[thisTarget] = string(cmdOutput.Bytes()) - // // TODO(mierdin): Something worried me here (can't remember what) regarding - // // if only some agents were running native testlets, does this wait group methodology work? - // // Sorry it's not clear, I have had a bit too much wine. - // defer wg.Done() - - // nativeTestlet, err := testlets.NewTestlet(tr.Testlet) - // if err != nil { - // //TODO(mierdin) do something - // } - - // metrics, err := nativeTestlet.Run("8.8.8.8", []string{"-c 10", "-s"}, ett.TimeLimit) - // //log.Error(nativeTestlet.RunFunction) - // if err != nil { - // log.Errorf("Testlet completed with error '%s'", err) - // gatheredData[thisTarget] = "error" - // } - - // // The metrics infrastructure requires that we collect metrics as a JSON string - // // (which is a result of building non-native testlets in early versions of ToDD) - // // So let's convert, and add to gatheredData - // metrics_json, err := json.Marshal(metrics) - // if err != nil { - // //TODO(mierdin) do something - // } - // gatheredData[thisTarget] = string(metrics_json) - }() - } wg.Wait() diff --git a/agent/tasks/installtestrun.go b/agent/tasks/installtestrun.go index 96bb662..355e45e 100644 --- a/agent/tasks/installtestrun.go +++ b/agent/tasks/installtestrun.go @@ -45,16 +45,15 @@ func (itt InstallTestRunTask) Run() error { var testlet_path string - // Determine if this is a valid native testlet - //_, err := testlets.NewTestlet(itt.Tr.Testlet) + // Determine if this is a native testlet isNative, newName := testlets.IsNativeTestlet(itt.Tr.Testlet) - // Not a native testlet - attempt to run check mode on testlet in filesystem + + // If we're running a native testlet, we want testlet_path to simply be the testlet name + // (since it is a requirement that the native-Go testlets are in the PATH) + // If the testlet is not native, we can get the full path. if isNative { - // Nothing to do, as we're using a native testlet log.Infof("%s is a native testlet - installing testrun.", itt.Tr.Testlet) - //itt.Tr.Testlet = newName testlet_path = newName - } else { // Generate path to testlet and make sure it exists. testlet_path = fmt.Sprintf("%s/assets/testlets/%s", itt.Config.LocalResources.OptDir, itt.Tr.Testlet) diff --git a/agent/testing/bashtestlets/notatallping b/agent/testing/bashtestlets/notatallping deleted file mode 100755 index aed17d4..0000000 --- a/agent/testing/bashtestlets/notatallping +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env bash - -# Copyright 2016 Matt Oswalt. Use or modification of this -# source code is governed by the license provided here: -# https://github.com/Mierdin/todd/blob/master/LICENSE - -check() { - echo "Running in check mode" - command -v ping >/dev/null 2>&1 || { echo >&2 "Ping is not installed. Exiting."; exit 1; } - echo "Check mode PASSED" - exit 0 -} - -if [[ $1 == "check" ]]; - then - check -fi - -# The first argument after the testlet name will ALWAYS be the target. So, $1 is always the target, and all arguments from $2 and up should be passed into the app command -app_args="$@" -app_args=${app_args#$0 } -app_args=${app_args#$1 } - -app_run_command="ping $app_args $1" - -output=$($app_run_command) - -packet_loss=$(echo $output | tr '\n' ' ' | sed -n 's/.* \(.*\)% packet loss.*/\1/p') -avg_latency=$(echo $output | tr '\n' ' ' | sed -n 's/.*dev = .*\/\(.*\)\/.*\/.*/\1/p') - -teslet_data='{"packet_loss_percentage":"'$packet_loss'", "avg_latency_ms":"'$avg_latency'"}' -echo $teslet_data \ No newline at end of file diff --git a/agent/testing/testlets/testlets.go b/agent/testing/testlets/testlets.go index 7bcafe1..ea35bce 100644 --- a/agent/testing/testlets/testlets.go +++ b/agent/testing/testlets/testlets.go @@ -6,16 +6,37 @@ import ( "sort" "sync" "time" - //"sync/atomic" log "github.com/Sirupsen/logrus" ) +// NOTE +// +// Early efforts to build native-Go testlets involved the embedding of testlet logic into the +// ToDD agent itself. As a result, it was important to build some reusable infrastructure so that goroutines +// running testlet code inside the agent could be controlled, and that new testlets could benefit from this +// infrastructure. +// +// Since then, the decision was made to keep testlets as their own separate binaries. Despite this, we can still benefit +// from having them in Go because it is much more cross-platform than bash scripts. +// +// These testlets are in their own repositories, and they do actually use some of the logic below, just not as meaningfully +// and comprehensively as they would have if they were baked in to the agent. Those testlets will still vendor this code +// and leverage the "Testlet" interface so that in the future, if we want to roll these into the todd-agent, those +// testlets will already conform to the standard provided below. + var ( - testletsMu sync.RWMutex - testlets = make(map[string]Testlet) - done = make(chan error) // Used from within the goroutine to inform the infrastructure it has finished - kill = make(chan bool) // Used from outside the goroutine to inform the goroutine to stop + testletsMu sync.RWMutex + testlets = make(map[string]Testlet) + done = make(chan error) // Used from within the goroutine to inform the infrastructure it has finished + kill = make(chan bool) // Used from outside the goroutine to inform the goroutine to stop + + // This map provides name redirection so that the native testlets can use names that don't + // conflict with existing system tools (i.e. using "toddping" instead of "ping") but users + // can still refer to the testlets using simple names. + // + // In short, users refer to the testlet by and this map will redirect to the + // actual binary name nativeTestlets = map[string]string{ "ping": "toddping", } @@ -73,7 +94,8 @@ func (b BaseTestlet) Run(target string, args []string, timeLimit int) (map[strin // kill = make(chan bool) // TODO(mierdin): Based on experimentation, this will keep running even if this function returns. - // Need to be sure about how this ends. Also might want to evaluate the same for the existing non-native model, likely has the same issue + // Need to be sure about how this ends. Also might want to evaluate the same for the existing + // non-native model, likely has the same issue go func() { theseMetrics, err := b.RunFunction(target, args, kill) metrics = theseMetrics //TODO(mierdin): Gross. diff --git a/scripts/buildtestlets.sh b/scripts/buildtestlets.sh deleted file mode 100755 index 0875fb1..0000000 --- a/scripts/buildtestlets.sh +++ /dev/null @@ -1,91 +0,0 @@ -#!/bin/bash - -# Copyright 2016 Matt Oswalt. Use or modification of this -# source code is governed by the license provided here: -# https://github.com/mierdin/todd/blob/master/LICENSE - -# This script downloads ToDD testlets prior to compile - -set -e -set -u -set -o pipefail - - -testlets=( - 'github.com/toddproject/todd-nativetestlet-ping' - ) - - -#rm -rf testlettemp && mkdir testlettemp && cd testlettemp - -for i in "${testlets[@]}" -do - #echo "Installing $i" - # git clone $i --quiet - go get -d -u $i/... -done - -# cd .. - -# rm -rf agent/testing/downloaded_testlets/ && mkdir agent/testing/downloaded_testlets - -# for dir in ./testlettemp/*/ -# do -# dir=${dir%*/} -# cp testlettemp/${dir##*/}/testlet/* agent/testing/downloaded_testlets -# #echo ${dir##*/} - - - -# testletdir="$(pwd)/$dir" -# #echo $testletdir - -# ln -s $testletdir/vendor/ $testletdir/vendor/src - -# # echo ./testlettemp/todd-nativetestlet-ping - -# # Append this vendor directory to GOPATH -# # TODO need to do some cleanup somewhere to remove this -# if [[ ":$GOPATH:" != *":$testletdir/vendor:"* ]]; then -# echo "export GOPATH=$GOPATH:$testletdir/vendor" -# fi - - -# done - - - - -# rm -rf testlettemp - - - -# rebuild plugins: -# _debug "removing: ${plugin_dir:?}/*" -# rm -rf "${plugin_dir:?}/"* -# mkdir -p "${plugin_dir}" - -# _info "building plugins" -# find "${__proj_dir}/plugin/" -type d -iname "snap-*" -print0 | xargs -0 -n 1 -I{} "${__dir}/build_plugin.sh" {} - -#--------- - - - -# __dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -# __proj_dir="$(dirname "$__dir")" - -# # shellcheck source=scripts/common.sh -# . "${__dir}/common.sh" - -# build_dir="${__proj_dir}/build" -# plugin_dir="${build_dir}/plugin" - -# plugin_src_path=$1 -# plugin_name=$(basename "${plugin_src_path}") -# go_build=(go build -a -ldflags "-w") - -# _debug "plugin source: ${plugin_src_path}" -# _info "building ${plugin_name}" - -# (cd "${plugin_src_path}" && "${go_build[@]}" -o "${plugin_dir}/${plugin_name}" . || exit 1) \ No newline at end of file diff --git a/scripts/gettestlets.sh b/scripts/gettestlets.sh new file mode 100755 index 0000000..c8e56ac --- /dev/null +++ b/scripts/gettestlets.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +# Copyright 2016 Matt Oswalt. Use or modification of this +# source code is governed by the license provided here: +# https://github.com/mierdin/todd/blob/master/LICENSE + +# This script installs ToDD-native testlets + +set -e +set -u +set -o pipefail + +# Install these testlets - comment out specific testlets to +# control what's installed +testlets=( + 'github.com/toddproject/todd-nativetestlet-ping' +) + +for i in "${testlets[@]}" +do + echo "Installing $i" + go get -u $i/cmd/... +done From 99c253355d5d340ea757ca7d4c924a9af45d61b3 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Mon, 22 Aug 2016 23:43:58 -0700 Subject: [PATCH 041/120] Added note about future development efforts around testlets Signed-off-by: Matt Oswalt --- Makefile | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index adb170b..ac80285 100644 --- a/Makefile +++ b/Makefile @@ -11,8 +11,11 @@ build: docker build -t mierdin/todd -f Dockerfile . compile: - # TODO(mierdin): Need to support some kind of mode that allows for development (i.e. not downloading the testlets live, but rather from a directory?) - + # TODO(mierdin): Need to support some kind of mode that allows for development. + # The current gettestlets.sh script downloads the testlets from Github, meaning + # a developer would already have to have changes pushed to those repos' master + # Looking for something like devstack, etc. + # # Installing testlets ./scripts/gettestlets.sh From 4c0d0ce79fce08cc3f2f1072317fa1e4bb8bb99d Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Mon, 22 Aug 2016 23:51:29 -0700 Subject: [PATCH 042/120] Some fixes from reviewing the PR Signed-off-by: Matt Oswalt --- .gitignore | 1 - Vagrantfile | 2 +- agent/tasks/executetestrun.go | 21 ++++++++------------- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index 9896fa8..925635d 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,3 @@ preso_secret/ client/repeattest.sh *.out *.DS_Store* -agent/testing/downloaded_testlets/ \ No newline at end of file diff --git a/Vagrantfile b/Vagrantfile index a9efd37..9317dee 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -6,7 +6,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.box = "trusty64" config.vm.box_url = "http://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-amd64-vagrant-disk1.box" - config.vm.synced_folder '../../', '/home/vagrant/go/src/github.com', nfs: true + config.vm.synced_folder '.', '/home/vagrant/go/src/github.com/Mierdin/todd', nfs: true config.vm.provider "virtualbox" do |v| diff --git a/agent/tasks/executetestrun.go b/agent/tasks/executetestrun.go index de1ebbc..90776bb 100644 --- a/agent/tasks/executetestrun.go +++ b/agent/tasks/executetestrun.go @@ -25,16 +25,6 @@ import ( "github.com/Mierdin/todd/config" ) -var ( - // gatheredData represents test data from this agent for all targets. - // Key is target name, value is JSON output from testlet for that target - // This is reset to a blank map every time ExecuteTestRunTask is called - gatheredData = make(map[string]string) - - // Use a wait group to ensure that all of the testlets have a chance to finish - wg sync.WaitGroup -) - // ExecuteTestRunTask defines this particular task. type ExecuteTestRunTask struct { BaseTask @@ -48,8 +38,13 @@ type ExecuteTestRunTask struct { // a testrun will be executed once per target, all in parallel. func (ett ExecuteTestRunTask) Run() error { - // Make sure we're working with a clean slate - gatheredData = map[string]string{} + // gatheredData represents test data from this agent for all targets. + // Key is target name, value is JSON output from testlet for that target + // This is reset to a blank map every time ExecuteTestRunTask is called + gatheredData := map[string]string{} + + // Use a wait group to ensure that all of the testlets have a chance to finish + var wg sync.WaitGroup // Waiting three seconds to ensure all the agents have their tasks before we potentially hammer the network // TODO(mierdin): This is a temporary measure - in the future, testruns will be executed via time schedule, @@ -110,7 +105,7 @@ func (ett ExecuteTestRunTask) Run() error { // Execute collector cmd.Start() - // TODO(mierdin): Does this need to be a buffered channel? + // TODO(mierdin): Why is this a buffered channel? Is this necessary? done := make(chan error, 1) go func() { done <- cmd.Wait() From 5056f30dcb3a2cf7a20e251b620c8220217f2f4b Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Tue, 23 Aug 2016 22:43:25 -0700 Subject: [PATCH 043/120] Moved testlets outside to the testing package Signed-off-by: Matt Oswalt --- agent/testing/{testlets => }/testlets.go | 48 ++++++++++++------------ 1 file changed, 23 insertions(+), 25 deletions(-) rename agent/testing/{testlets => }/testlets.go (79%) diff --git a/agent/testing/testlets/testlets.go b/agent/testing/testlets.go similarity index 79% rename from agent/testing/testlets/testlets.go rename to agent/testing/testlets.go index ea35bce..dc4bb51 100644 --- a/agent/testing/testlets/testlets.go +++ b/agent/testing/testlets.go @@ -1,4 +1,12 @@ -package testlets +/* + ToDD task - set keyvalue pair in cache + + Copyright 2016 Matt Oswalt. Use or modification of this + source code is governed by the license provided here: + https://github.com/Mierdin/todd/blob/master/LICENSE +*/ + +package testing import ( "errors" @@ -17,13 +25,14 @@ import ( // running testlet code inside the agent could be controlled, and that new testlets could benefit from this // infrastructure. // -// Since then, the decision was made to keep testlets as their own separate binaries. Despite this, we can still benefit -// from having them in Go because it is much more cross-platform than bash scripts. +// Since then, the decision was made to keep testlets as their own separate binaries. // // These testlets are in their own repositories, and they do actually use some of the logic below, just not as meaningfully -// and comprehensively as they would have if they were baked in to the agent. Those testlets will still vendor this code -// and leverage the "Testlet" interface so that in the future, if we want to roll these into the todd-agent, those -// testlets will already conform to the standard provided below. +// and comprehensively as they would have if they were baked in to the agent. The development standard for all "blessed" +// testlets will still ensure that they use this interface, so that if we decide to bake them into the agent in the future, +// they'll already conform. +// +// (The vast majority of this code was inspired by the database drivers implementation in the stdlib) var ( testletsMu sync.RWMutex @@ -69,7 +78,6 @@ type Testlet interface { // without worrying about things like managing goroutines or channels. That's all // managed by the "Run" or "Kill" functions RunTestlet(string, []string, chan bool) (map[string]string, error) - // TODO(mierdin): is this really the best name for it? Maybe something that's less confusing, less like "Run" // All testlets must be able to stop operation when sent a Kill command. Kill() error @@ -89,16 +97,12 @@ func (b BaseTestlet) Run(target string, args []string, timeLimit int) (map[strin var metrics map[string]string - // TODO(mierdin): ensure channel is nil - // done = make(chan error) - // kill = make(chan bool) + // Ensure control channels are empty + done := make(chan error) + kill := make(chan bool) - // TODO(mierdin): Based on experimentation, this will keep running even if this function returns. - // Need to be sure about how this ends. Also might want to evaluate the same for the existing - // non-native model, likely has the same issue go func() { - theseMetrics, err := b.RunFunction(target, args, kill) - metrics = theseMetrics //TODO(mierdin): Gross. + metrics, err := b.RunFunction(target, args, kill) done <- err }() @@ -112,7 +116,7 @@ func (b BaseTestlet) Run(target string, args []string, timeLimit int) (map[strin case err := <-done: if err != nil { - return map[string]string{}, errors.New("testlet error") // TODO(mierdin): elaborate? + return map[string]string{}, errors.New("testlet error") } else { log.Debugf("Testlet completed without error") return metrics, nil @@ -120,13 +124,10 @@ func (b BaseTestlet) Run(target string, args []string, timeLimit int) (map[strin } } +// Kill is currently unimplemented. This will have to be coordinated with "Run". Basically +// you need a way to kill this testlet (and that's really only possible when running +// async) Probably just want to set the channel to something so the select within "Run" will execute func (b BaseTestlet) Kill() error { - // TODO (mierdin): This will have to be coordinated with the task above. Basically - // you need a way to kill this testlet (and that's really only possible when running - // async) - - // Probably just want to set the channel to something so the select within "Run" will execute - return nil } @@ -144,9 +145,6 @@ func IsNativeTestlet(name string) (bool, string) { func NewTestlet(name string) (Testlet, error) { if testlet, ok := testlets[name]; ok { - - // testlet.runFunction = testlet.run - return testlet, nil } else { return nil, errors.New( From 7adfee103880c8de09b4468af5b86c38c4bb78af Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Sun, 18 Sep 2016 02:06:28 -0700 Subject: [PATCH 044/120] Updated testlets.go Signed-off-by: Matt Oswalt --- agent/testing/testlets.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/agent/testing/testlets.go b/agent/testing/testlets.go index dc4bb51..0a5f2c6 100644 --- a/agent/testing/testlets.go +++ b/agent/testing/testlets.go @@ -103,6 +103,14 @@ func (b BaseTestlet) Run(target string, args []string, timeLimit int) (map[strin go func() { metrics, err := b.RunFunction(target, args, kill) +<<<<<<< 5056f30dcb3a2cf7a20e251b620c8220217f2f4b:agent/testing/testlets.go +======= + + // TODO(mierdin): avoiding a "declared and not used" error for now + // If this code is ever actually used, it should be modified to make "done" a channel that returns the metrics, so it's actually used (just an idea) + log.Error(metrics) + +>>>>>>> Updated testlets.go:agent/testing/testlets/testlets.go done <- err }() From 287aa03c127fa5a60db0684c4e4340d6cec19f62 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Mon, 19 Sep 2016 00:56:17 -0700 Subject: [PATCH 045/120] Various moves Signed-off-by: Matt Oswalt --- agent/testing/{reporting.go => testing.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename agent/testing/{reporting.go => testing.go} (100%) diff --git a/agent/testing/reporting.go b/agent/testing/testing.go similarity index 100% rename from agent/testing/reporting.go rename to agent/testing/testing.go From 7c67377ed7d6b0b15b68fa33052056e492f063da Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Mon, 19 Sep 2016 22:49:40 -0700 Subject: [PATCH 046/120] removed all old work from testlets (moved to archive) Signed-off-by: Matt Oswalt --- agent/testing/testlets.go | 51 +++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/agent/testing/testlets.go b/agent/testing/testlets.go index 0a5f2c6..0ad300c 100644 --- a/agent/testing/testlets.go +++ b/agent/testing/testlets.go @@ -9,13 +9,8 @@ package testing import ( - "errors" - "fmt" - "sort" "sync" - "time" - - log "github.com/Sirupsen/logrus" + // log "github.com/Sirupsen/logrus" ) // NOTE @@ -55,8 +50,8 @@ var ( // go and compiled with the agent type Testlet interface { - // Run is the "workflow" function for a testlet. It handles running - // the RunTestlet function asynchronously and managing the state therein. + // Run is the "workflow" function for a testlet. All testing takes place here + // (or in a function called within) // // Params are // target (string) @@ -64,26 +59,28 @@ type Testlet interface { // timeLimit (int in seconds) // // Returns: - // metrics (map[string]interface{}) + // metrics (map[string]string) // (name of metric is key, value is metric value) - // - // Keep as much logic out of here as possible. All native testlets - // must support a "Kill" method, so it's best to implement core testlet - // logic in a separate function so that the Run and Kill commands can manage - // execution of that logic in a goroutine Run(string, []string, int) (map[string]string, error) - - // RunTestlet is designed to be the one-stop shop for testlet logic. - // The developer of a native testlet just needs to implement the testlet logic here, - // without worrying about things like managing goroutines or channels. That's all - // managed by the "Run" or "Kill" functions - RunTestlet(string, []string, chan bool) (map[string]string, error) - - // All testlets must be able to stop operation when sent a Kill command. - Kill() error } -type rtfunc func(target string, args []string, kill chan bool) (map[string]string, error) +// NOTE +// +// Early efforts to build native-Go testlets involved the embedding of testlet logic into the +// ToDD agent itself. As a result, it was important to build some reusable infrastructure so that goroutines +// running testlet code within the agent could be controlled, and that new testlets could benefit from this +// infrastructure. +// +// Since then, the decision was made to keep testlets as their own separate binaries. +// +// These testlets are in their own repositories, and they do actually use some of the logic below, just not as meaningfully +// and comprehensively as they would have if they were baked in to the agent. The development standard for all "blessed" +// testlets will still ensure that they use this interface, so that if we decide to bake them into the agent in the future, +// they'll already conform. +// +// (The vast majority of this code was inspired by the database drivers implementation in the stdlib) + +type rtfunc func(target string, args []string, timeout int) (map[string]string, error) type BaseTestlet struct { @@ -92,6 +89,7 @@ type BaseTestlet struct { RunFunction rtfunc } +<<<<<<< 287aa03c127fa5a60db0684c4e4340d6cec19f62:agent/testing/testlets.go // Run takes care of running the testlet function and managing it's operation given the parameters provided func (b BaseTestlet) Run(target string, args []string, timeLimit int) (map[string]string, error) { @@ -139,6 +137,8 @@ func (b BaseTestlet) Kill() error { return nil } +======= +>>>>>>> removed all old work from testlets (moved to archive):agent/testing/testlets/testlets.go // IsNativeTestlet polls the list of registered native testlets, and returns // true if the referenced name exists func IsNativeTestlet(name string) (bool, string) { @@ -148,6 +148,7 @@ func IsNativeTestlet(name string) (bool, string) { return false, "" } } +<<<<<<< 287aa03c127fa5a60db0684c4e4340d6cec19f62:agent/testing/testlets.go //NewTestlet produces a new testlet based on the "name" param func NewTestlet(name string) (Testlet, error) { @@ -195,3 +196,5 @@ func Testlets() []string { sort.Strings(list) return list } +======= +>>>>>>> removed all old work from testlets (moved to archive):agent/testing/testlets/testlets.go From 3d96d8acf8e0b76c346ddcdd4fe3fcd51ed08661 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Mon, 19 Sep 2016 23:03:05 -0700 Subject: [PATCH 047/120] Reorganized testing logic Signed-off-by: Matt Oswalt --- agent/tasks/executetestrun.go | 4 +- agent/tasks/installtestrun.go | 4 +- agent/testing/testing.go | 118 ++++++++++++++++++---------------- cmd/todd-agent/main.go | 54 ++++++++++++++-- 4 files changed, 116 insertions(+), 64 deletions(-) diff --git a/agent/tasks/executetestrun.go b/agent/tasks/executetestrun.go index 90776bb..d3cf2be 100644 --- a/agent/tasks/executetestrun.go +++ b/agent/tasks/executetestrun.go @@ -21,7 +21,7 @@ import ( log "github.com/Sirupsen/logrus" "github.com/Mierdin/todd/agent/cache" - "github.com/Mierdin/todd/agent/testing/testlets" + "github.com/Mierdin/todd/agent/testing" "github.com/Mierdin/todd/config" ) @@ -66,7 +66,7 @@ func (ett ExecuteTestRunTask) Run() error { wg.Add(len(tr.Targets)) var testlet_path string - isNative, newTestletName := testlets.IsNativeTestlet(tr.Testlet) + isNative, newTestletName := testing.IsNativeTestlet(tr.Testlet) // If we're running a native testlet, we want testlet_path to simply be the testlet name // (since it is a requirement that the native-Go testlets are in the PATH) diff --git a/agent/tasks/installtestrun.go b/agent/tasks/installtestrun.go index 355e45e..ccfd711 100644 --- a/agent/tasks/installtestrun.go +++ b/agent/tasks/installtestrun.go @@ -20,7 +20,7 @@ import ( "github.com/Mierdin/todd/agent/cache" "github.com/Mierdin/todd/agent/defs" - "github.com/Mierdin/todd/agent/testing/testlets" + "github.com/Mierdin/todd/agent/testing" "github.com/Mierdin/todd/config" ) @@ -46,7 +46,7 @@ func (itt InstallTestRunTask) Run() error { var testlet_path string // Determine if this is a native testlet - isNative, newName := testlets.IsNativeTestlet(itt.Tr.Testlet) + isNative, newName := testing.IsNativeTestlet(itt.Tr.Testlet) // If we're running a native testlet, we want testlet_path to simply be the testlet name // (since it is a requirement that the native-Go testlets are in the PATH) diff --git a/agent/testing/testing.go b/agent/testing/testing.go index 58489ed..9155e9d 100644 --- a/agent/testing/testing.go +++ b/agent/testing/testing.go @@ -1,68 +1,78 @@ /* - ToDD Agent - Test Reporting + ToDD testing package - This file contains functions that watch the agent cache and upload test data when present. + Contains infrastructure running testlets as well as maintaining + conformance for other native-Go testlet projects - Copyright 2016 Matt Oswalt. Use or modification of this - source code is governed by the license provided here: - https://github.com/Mierdin/todd/blob/master/LICENSE + Copyright 2016 Matt Oswalt. Use or modification of this + source code is governed by the license provided here: + https://github.com/Mierdin/todd/blob/master/LICENSE */ package testing -import ( - "errors" - "time" +var ( - log "github.com/Sirupsen/logrus" - - "github.com/Mierdin/todd/agent/cache" - "github.com/Mierdin/todd/agent/responses" - "github.com/Mierdin/todd/comms" - "github.com/Mierdin/todd/config" + // This map provides name redirection so that the native testlets can use names that don't + // conflict with existing system tools (i.e. using "toddping" instead of "ping") but users + // can still refer to the testlets using simple names. + // + // In short, users refer to the testlet by and this map will redirect to the + // actual binary name + nativeTestlets = map[string]string{ + "ping": "toddping", + } ) -// WatchForFinishedTestRuns simply watches the local cache for any test runs that have test data. -// It will periodically look at the table and send any present test data back to the server as a response. -// When the server has successfully received this data, it will send a task back to this specific agent -// to delete this row from the cache. -func WatchForFinishedTestRuns(cfg config.Config) error { - - var ac = cache.NewAgentCache(cfg) - - agentUuid := ac.GetKeyValue("uuid") - - for { - - time.Sleep(5000 * time.Millisecond) - - testruns, err := ac.GetFinishedTestRuns() - if err != nil { - log.Error("Problem retrieving finished test runs") - return errors.New("Problem retrieving finished test runs") - } - - for testUuid, testData := range testruns { - - log.Debug("Found ripe testrun: ", testUuid) - - var utdr = responses.UploadTestDataResponse{ - TestUuid: testUuid, - TestData: testData, - } - utdr.AgentUuid = agentUuid - utdr.Type = "TestData" //TODO(mierdin): This is an extra step. Maybe a factory function for the task could help here? - - tc, err := comms.NewToDDComms(cfg) - if err != nil { - return err - } - tc.CommsPackage.SendResponse(utdr) +// Testlet defines what a testlet should look like if built in native +// go and compiled with the agent +type Testlet interface { + + // Run is the "workflow" function for a testlet. All testing takes place here + // (or in a function called within) + // + // Params are + // target (string) + // args ([]string) + // timeLimit (int in seconds) + // + // Returns: + // metrics (map[string]string) + // (name of metric is key, value is metric value) + Run(string, []string, int) (map[string]string, error) +} - } +// NOTE +// +// Early efforts to build native-Go testlets involved the embedding of testlet logic into the +// ToDD agent itself. As a result, it was important to build some reusable infrastructure so that goroutines +// running testlet code within the agent could be controlled, and that new testlets could benefit from this +// infrastructure. +// +// Since then, the decision was made to keep testlets as their own separate binaries. +// +// These testlets are in their own repositories, and they do actually use some of the logic below, just not as meaningfully +// and comprehensively as they would have if they were baked in to the agent. The development standard for all "blessed" +// testlets will still ensure that they use this interface, so that if we decide to bake them into the agent in the future, +// they'll already conform. +// +// (The vast majority of this code was inspired by the database drivers implementation in the stdlib) + +type rtfunc func(target string, args []string, timeout int) (map[string]string, error) + +type BaseTestlet struct { + + // rtfunc is a type that will store our RunTestlet function. It is the responsibility + // of the "child" testlet to set this value upon creation + RunFunction rtfunc +} +// IsNativeTestlet polls the list of registered native testlets, and returns +// true if the referenced name exists +func IsNativeTestlet(name string) (bool, string) { + if _, ok := nativeTestlets[name]; ok { + return true, nativeTestlets[name] + } else { + return false, "" } - - return nil - } diff --git a/cmd/todd-agent/main.go b/cmd/todd-agent/main.go index a7f527e..aa39c81 100644 --- a/cmd/todd-agent/main.go +++ b/cmd/todd-agent/main.go @@ -9,6 +9,7 @@ package main import ( + "errors" "flag" "fmt" "os" @@ -19,14 +20,10 @@ import ( "github.com/Mierdin/todd/agent/cache" "github.com/Mierdin/todd/agent/defs" "github.com/Mierdin/todd/agent/facts" - "github.com/Mierdin/todd/agent/testing" + "github.com/Mierdin/todd/agent/responses" "github.com/Mierdin/todd/comms" "github.com/Mierdin/todd/config" "github.com/Mierdin/todd/hostresources" - // Testlet imports (importing these packages registers the testlets) - // Need to make this dynamic if possible (at compile time of course) - // TODO Not necessary anymore - //_ "github.com/toddproject/todd-nativetestlet-ping/testlet" ) // Command-line Arguments @@ -70,7 +67,7 @@ func main() { log.Infof("ToDD Agent Activated: %s", uuid) // Start test data reporting service - go testing.WatchForFinishedTestRuns(cfg) + go watchForFinishedTestRuns(cfg) // Construct comms package tc, err := comms.NewToDDComms(cfg) @@ -124,3 +121,48 @@ func main() { } } + +// watchForFinishedTestRuns simply watches the local cache for any test runs that have test data. +// It will periodically look at the table and send any present test data back to the server as a response. +// When the server has successfully received this data, it will send a task back to this specific agent +// to delete this row from the cache. +func watchForFinishedTestRuns(cfg config.Config) error { + + var ac = cache.NewAgentCache(cfg) + + agentUuid := ac.GetKeyValue("uuid") + + for { + + time.Sleep(5000 * time.Millisecond) + + testruns, err := ac.GetFinishedTestRuns() + if err != nil { + log.Error("Problem retrieving finished test runs") + return errors.New("Problem retrieving finished test runs") + } + + for testUuid, testData := range testruns { + + log.Debug("Found ripe testrun: ", testUuid) + + var utdr = responses.UploadTestDataResponse{ + TestUuid: testUuid, + TestData: testData, + } + utdr.AgentUuid = agentUuid + utdr.Type = "TestData" //TODO(mierdin): This is an extra step. Maybe a factory function for the task could help here? + + tc, err := comms.NewToDDComms(cfg) + if err != nil { + return err + } + tc.CommsPackage.SendResponse(utdr) + + } + + } + + return nil + +} From 6cb591cab3629986fb9d626292d54826270ceab9 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Tue, 20 Sep 2016 14:51:01 -0700 Subject: [PATCH 048/120] updated comment regarding future development script Signed-off-by: Matt Oswalt --- Makefile | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index ac80285..3352712 100644 --- a/Makefile +++ b/Makefile @@ -11,11 +11,13 @@ build: docker build -t mierdin/todd -f Dockerfile . compile: - # TODO(mierdin): Need to support some kind of mode that allows for development. - # The current gettestlets.sh script downloads the testlets from Github, meaning - # a developer would already have to have changes pushed to those repos' master - # Looking for something like devstack, etc. + # TODO(mierdin): The current gettestlets.sh script downloads the testlets from + # Github, meaning a developer would already have to have changes pushed to + # those repos' master. # + # In a follow-up patch, a script will be provided that allows for rapid development + # (uses local repositories instead of pulling from GH). Something like devstack. + # Installing testlets ./scripts/gettestlets.sh From 9d8e7ae179b7d6725c40b45f1586a5646849cd8f Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Tue, 20 Sep 2016 15:06:44 -0700 Subject: [PATCH 049/120] removed sudo from sysctl command in makefile Signed-off-by: Matt Oswalt --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 3352712..c021bdc 100644 --- a/Makefile +++ b/Makefile @@ -54,4 +54,4 @@ configureenv: chmod -R 777 /opt/todd # If on Linux, enable ping testlet functionality - sudo sysctl -w net.ipv4.ping_group_range="0 12345" + sysctl -w net.ipv4.ping_group_range="0 12345" From 1c83aea5a27288449fef64d3dee61823da3ac418 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Tue, 20 Sep 2016 17:05:26 -0700 Subject: [PATCH 050/120] Updates to makefile and vagrantfile Signed-off-by: Matt Oswalt --- Makefile | 2 +- Vagrantfile | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c021bdc..7f08d9c 100644 --- a/Makefile +++ b/Makefile @@ -54,4 +54,4 @@ configureenv: chmod -R 777 /opt/todd # If on Linux, enable ping testlet functionality - sysctl -w net.ipv4.ping_group_range="0 12345" + sysctl -w net.ipv4.ping_group_range="0 0" || echo "Unable to set kernel parameters to allow ping. Some testlets may not work." diff --git a/Vagrantfile b/Vagrantfile index 9317dee..dc26d0e 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -7,6 +7,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.box = "trusty64" config.vm.box_url = "http://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-amd64-vagrant-disk1.box" config.vm.synced_folder '.', '/home/vagrant/go/src/github.com/Mierdin/todd', nfs: true + config.vm.synced_folder '../../toddproject/todd-nativetestlet-ping', '/home/vagrant/go/src/github.com/toddproject/todd-nativetestlet-ping', nfs: true config.vm.provider "virtualbox" do |v| From bd0f9f7976ffeb65fd8f19984c3c4a1a7c5d128c Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Thu, 22 Sep 2016 14:10:43 -0700 Subject: [PATCH 051/120] Added script to add socket capabilities to testlet binaries on install Signed-off-by: Matt Oswalt --- Makefile | 8 +++++--- scripts/set-testlet-capabilities.sh | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 scripts/set-testlet-capabilities.sh diff --git a/Makefile b/Makefile index 7f08d9c..3338257 100644 --- a/Makefile +++ b/Makefile @@ -24,8 +24,6 @@ compile: # Installing ToDD go install ./cmd/... -install: configureenv - fmt: go fmt github.com/mierdin/todd/... @@ -47,7 +45,11 @@ start: compile # that's why "server-int.cfg" and "agent-int.cfg" are being used here. start-containers.sh 3 /etc/todd/server-int.cfg /etc/todd/agent-int.cfg -configureenv: +install: + + # Set testlet capabilities + ./scripts/set-testlet-capabilities.sh + # Copy configs if etc and /etc/todd aren't linked if ! [ "etc" -ef "/etc/todd" ]; then mkdir -p /etc/todd && cp -f ./etc/{agent,server}.cfg /etc/todd/; fi mkdir -p /opt/todd/{agent,server}/assets/{factcollectors,testlets} diff --git a/scripts/set-testlet-capabilities.sh b/scripts/set-testlet-capabilities.sh new file mode 100644 index 0000000..f37a749 --- /dev/null +++ b/scripts/set-testlet-capabilities.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +# Copyright 2016 Matt Oswalt. Use or modification of this +# source code is governed by the license provided here: +# https://github.com/mierdin/todd/blob/master/LICENSE + +# This script configures appropriate capabilities for testlet binaries + +set -e +set -u +set -o pipefail + +# Ensure GOPATH is set +: ${GOPATH:?"Please ensure GOPATH is set, and run sudo with -E when performing 'make install'"} + +# Enable raw socket capabilities on toddping +setcap cap_net_raw+ep $GOPATH/bin/toddping \ No newline at end of file From ca5664243583840fea5bc45ff5185122f37f1828 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Thu, 22 Sep 2016 14:12:59 -0700 Subject: [PATCH 052/120] Made capabilities script exec Signed-off-by: Matt Oswalt --- scripts/set-testlet-capabilities.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 scripts/set-testlet-capabilities.sh diff --git a/scripts/set-testlet-capabilities.sh b/scripts/set-testlet-capabilities.sh old mode 100644 new mode 100755 From 89081e8b34a3bae676735f746c392d094c8448bc Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Thu, 22 Sep 2016 15:09:12 -0700 Subject: [PATCH 053/120] Added some comments about install process Signed-off-by: Matt Oswalt --- Makefile | 10 ++-------- scripts/gettestlets.sh | 2 ++ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 3338257..0b9318c 100644 --- a/Makefile +++ b/Makefile @@ -11,12 +11,6 @@ build: docker build -t mierdin/todd -f Dockerfile . compile: - # TODO(mierdin): The current gettestlets.sh script downloads the testlets from - # Github, meaning a developer would already have to have changes pushed to - # those repos' master. - # - # In a follow-up patch, a script will be provided that allows for rapid development - # (uses local repositories instead of pulling from GH). Something like devstack. # Installing testlets ./scripts/gettestlets.sh @@ -55,5 +49,5 @@ install: mkdir -p /opt/todd/{agent,server}/assets/{factcollectors,testlets} chmod -R 777 /opt/todd - # If on Linux, enable ping testlet functionality - sysctl -w net.ipv4.ping_group_range="0 0" || echo "Unable to set kernel parameters to allow ping. Some testlets may not work." + # If on Linux, enable ping testlet functionality (DEPRECATED in favor of granting socket capabilities on testlets) + # sysctl -w net.ipv4.ping_group_range="0 0" || echo "Unable to set kernel parameters to allow ping. Some testlets may not work." diff --git a/scripts/gettestlets.sh b/scripts/gettestlets.sh index c8e56ac..e34a431 100755 --- a/scripts/gettestlets.sh +++ b/scripts/gettestlets.sh @@ -19,5 +19,7 @@ testlets=( for i in "${testlets[@]}" do echo "Installing $i" + + # This retrieves from GH if it doesn't exist, but if it does, it installs the local copy. go get -u $i/cmd/... done From 2f2f2a3c0f5e8be3db8a124b56a7c10d6ddfd838 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Thu, 22 Sep 2016 16:50:03 -0700 Subject: [PATCH 054/120] Removed extra crap from rebase Signed-off-by: Matt Oswalt --- agent/testing/testlets.go | 200 -------------------------------------- 1 file changed, 200 deletions(-) delete mode 100644 agent/testing/testlets.go diff --git a/agent/testing/testlets.go b/agent/testing/testlets.go deleted file mode 100644 index 0ad300c..0000000 --- a/agent/testing/testlets.go +++ /dev/null @@ -1,200 +0,0 @@ -/* - ToDD task - set keyvalue pair in cache - - Copyright 2016 Matt Oswalt. Use or modification of this - source code is governed by the license provided here: - https://github.com/Mierdin/todd/blob/master/LICENSE -*/ - -package testing - -import ( - "sync" - // log "github.com/Sirupsen/logrus" -) - -// NOTE -// -// Early efforts to build native-Go testlets involved the embedding of testlet logic into the -// ToDD agent itself. As a result, it was important to build some reusable infrastructure so that goroutines -// running testlet code inside the agent could be controlled, and that new testlets could benefit from this -// infrastructure. -// -// Since then, the decision was made to keep testlets as their own separate binaries. -// -// These testlets are in their own repositories, and they do actually use some of the logic below, just not as meaningfully -// and comprehensively as they would have if they were baked in to the agent. The development standard for all "blessed" -// testlets will still ensure that they use this interface, so that if we decide to bake them into the agent in the future, -// they'll already conform. -// -// (The vast majority of this code was inspired by the database drivers implementation in the stdlib) - -var ( - testletsMu sync.RWMutex - testlets = make(map[string]Testlet) - done = make(chan error) // Used from within the goroutine to inform the infrastructure it has finished - kill = make(chan bool) // Used from outside the goroutine to inform the goroutine to stop - - // This map provides name redirection so that the native testlets can use names that don't - // conflict with existing system tools (i.e. using "toddping" instead of "ping") but users - // can still refer to the testlets using simple names. - // - // In short, users refer to the testlet by and this map will redirect to the - // actual binary name - nativeTestlets = map[string]string{ - "ping": "toddping", - } -) - -// Testlet defines what a testlet should look like if built in native -// go and compiled with the agent -type Testlet interface { - - // Run is the "workflow" function for a testlet. All testing takes place here - // (or in a function called within) - // - // Params are - // target (string) - // args ([]string) - // timeLimit (int in seconds) - // - // Returns: - // metrics (map[string]string) - // (name of metric is key, value is metric value) - Run(string, []string, int) (map[string]string, error) -} - -// NOTE -// -// Early efforts to build native-Go testlets involved the embedding of testlet logic into the -// ToDD agent itself. As a result, it was important to build some reusable infrastructure so that goroutines -// running testlet code within the agent could be controlled, and that new testlets could benefit from this -// infrastructure. -// -// Since then, the decision was made to keep testlets as their own separate binaries. -// -// These testlets are in their own repositories, and they do actually use some of the logic below, just not as meaningfully -// and comprehensively as they would have if they were baked in to the agent. The development standard for all "blessed" -// testlets will still ensure that they use this interface, so that if we decide to bake them into the agent in the future, -// they'll already conform. -// -// (The vast majority of this code was inspired by the database drivers implementation in the stdlib) - -type rtfunc func(target string, args []string, timeout int) (map[string]string, error) - -type BaseTestlet struct { - - // rtfunc is a type that will store our RunTestlet function. It is the responsibility - // of the "child" testlet to set this value upon creation - RunFunction rtfunc -} - -<<<<<<< 287aa03c127fa5a60db0684c4e4340d6cec19f62:agent/testing/testlets.go -// Run takes care of running the testlet function and managing it's operation given the parameters provided -func (b BaseTestlet) Run(target string, args []string, timeLimit int) (map[string]string, error) { - - var metrics map[string]string - - // Ensure control channels are empty - done := make(chan error) - kill := make(chan bool) - - go func() { - metrics, err := b.RunFunction(target, args, kill) -<<<<<<< 5056f30dcb3a2cf7a20e251b620c8220217f2f4b:agent/testing/testlets.go -======= - - // TODO(mierdin): avoiding a "declared and not used" error for now - // If this code is ever actually used, it should be modified to make "done" a channel that returns the metrics, so it's actually used (just an idea) - log.Error(metrics) - ->>>>>>> Updated testlets.go:agent/testing/testlets/testlets.go - done <- err - }() - - // This select statement will block until one of these two conditions are met: - // - The testlet finishes, in which case the channel "done" will be receive a value - // - The configured time limit is exceeded (expected for testlets running in server mode) - select { - case <-time.After(time.Duration(timeLimit) * time.Second): - log.Debug("Successfully killed ") - return map[string]string{}, nil - - case err := <-done: - if err != nil { - return map[string]string{}, errors.New("testlet error") - } else { - log.Debugf("Testlet completed without error") - return metrics, nil - } - } -} - -// Kill is currently unimplemented. This will have to be coordinated with "Run". Basically -// you need a way to kill this testlet (and that's really only possible when running -// async) Probably just want to set the channel to something so the select within "Run" will execute -func (b BaseTestlet) Kill() error { - return nil -} - -======= ->>>>>>> removed all old work from testlets (moved to archive):agent/testing/testlets/testlets.go -// IsNativeTestlet polls the list of registered native testlets, and returns -// true if the referenced name exists -func IsNativeTestlet(name string) (bool, string) { - if _, ok := nativeTestlets[name]; ok { - return true, nativeTestlets[name] - } else { - return false, "" - } -} -<<<<<<< 287aa03c127fa5a60db0684c4e4340d6cec19f62:agent/testing/testlets.go - -//NewTestlet produces a new testlet based on the "name" param -func NewTestlet(name string) (Testlet, error) { - - if testlet, ok := testlets[name]; ok { - return testlet, nil - } else { - return nil, errors.New( - fmt.Sprintf("'%s' not currently supported as a native testlet"), - ) - } -} - -// Register makes a testlet available by the provided name. -// If Register is called twice with the same name or if testlet is nil, -// it will return an error -func Register(name string, testlet Testlet) error { - testletsMu.Lock() - defer testletsMu.Unlock() - if testlet == nil { - return errors.New("Register testlet is nil") - } - if _, dup := testlets[name]; dup { - return errors.New("Register called twice for testlet " + name) - } - testlets[name] = testlet - return nil -} - -func unregisterAllTestlets() { - testletsMu.Lock() - defer testletsMu.Unlock() - // For tests. - testlets = make(map[string]Testlet) -} - -// Testlets returns a sorted list of the names of the registered testlets. -func Testlets() []string { - testletsMu.RLock() - defer testletsMu.RUnlock() - var list []string - for name := range testlets { - list = append(list, name) - } - sort.Strings(list) - return list -} -======= ->>>>>>> removed all old work from testlets (moved to archive):agent/testing/testlets/testlets.go From 7235411f2c60f6fc58ed7058d52168e823ee216f Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Thu, 22 Sep 2016 16:56:34 -0700 Subject: [PATCH 055/120] Moved capabilities script to 'compile' Signed-off-by: Matt Oswalt --- Makefile | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 0b9318c..e5b8cf5 100644 --- a/Makefile +++ b/Makefile @@ -12,8 +12,9 @@ build: compile: - # Installing testlets + # Installing testlets and setting capabilities ./scripts/gettestlets.sh + ./scripts/set-testlet-capabilities.sh # Installing ToDD go install ./cmd/... @@ -41,9 +42,6 @@ start: compile install: - # Set testlet capabilities - ./scripts/set-testlet-capabilities.sh - # Copy configs if etc and /etc/todd aren't linked if ! [ "etc" -ef "/etc/todd" ]; then mkdir -p /etc/todd && cp -f ./etc/{agent,server}.cfg /etc/todd/; fi mkdir -p /opt/todd/{agent,server}/assets/{factcollectors,testlets} From 5e2dc41d58de3ab21dd6093f6b3cc3ffb43f2a32 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Thu, 22 Sep 2016 17:00:02 -0700 Subject: [PATCH 056/120] Removed old sysctl command from Makefile Signed-off-by: Matt Oswalt --- Makefile | 3 --- 1 file changed, 3 deletions(-) diff --git a/Makefile b/Makefile index e5b8cf5..be0b390 100644 --- a/Makefile +++ b/Makefile @@ -46,6 +46,3 @@ install: if ! [ "etc" -ef "/etc/todd" ]; then mkdir -p /etc/todd && cp -f ./etc/{agent,server}.cfg /etc/todd/; fi mkdir -p /opt/todd/{agent,server}/assets/{factcollectors,testlets} chmod -R 777 /opt/todd - - # If on Linux, enable ping testlet functionality (DEPRECATED in favor of granting socket capabilities on testlets) - # sysctl -w net.ipv4.ping_group_range="0 0" || echo "Unable to set kernel parameters to allow ping. Some testlets may not work." From 83de4a0b09febd44833435d0be71183ef9307d0e Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Thu, 22 Sep 2016 17:00:16 -0700 Subject: [PATCH 057/120] Added comment about folder redir to Vagrantfile Signed-off-by: Matt Oswalt --- Vagrantfile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Vagrantfile b/Vagrantfile index dc26d0e..6f13a6e 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -7,15 +7,17 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.box = "trusty64" config.vm.box_url = "http://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-amd64-vagrant-disk1.box" config.vm.synced_folder '.', '/home/vagrant/go/src/github.com/Mierdin/todd', nfs: true - config.vm.synced_folder '../../toddproject/todd-nativetestlet-ping', '/home/vagrant/go/src/github.com/toddproject/todd-nativetestlet-ping', nfs: true + # This is a temporary measure. Once https://github.com/Mierdin/todd/issues/72 is addressed, we can go back + # to a single folder being redirected (namely toddproject). For now, since ToDD is still in "Mierdin", + # we need to do it this way. + config.vm.synced_folder '../../toddproject/todd-nativetestlet-ping', '/home/vagrant/go/src/github.com/toddproject/todd-nativetestlet-ping', nfs: true config.vm.provider "virtualbox" do |v| v.memory = 2048 v.cpus = 2 end - config.vm.define "todddev" do |todddev| todddev.vm.host_name = "todddev" From 8e79ee75bb44edb8e0833ef88fa26c6b8e139614 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Thu, 22 Sep 2016 17:56:24 -0700 Subject: [PATCH 058/120] Moved capabilities back to 'install', where it's likely to get sudo (duh) Signed-off-by: Matt Oswalt --- Makefile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index be0b390..c2246aa 100644 --- a/Makefile +++ b/Makefile @@ -12,9 +12,8 @@ build: compile: - # Installing testlets and setting capabilities + # Installing testlets ./scripts/gettestlets.sh - ./scripts/set-testlet-capabilities.sh # Installing ToDD go install ./cmd/... @@ -42,6 +41,9 @@ start: compile install: + # Set capabilities on testlets + ./scripts/set-testlet-capabilities.sh + # Copy configs if etc and /etc/todd aren't linked if ! [ "etc" -ef "/etc/todd" ]; then mkdir -p /etc/todd && cp -f ./etc/{agent,server}.cfg /etc/todd/; fi mkdir -p /opt/todd/{agent,server}/assets/{factcollectors,testlets} From 954fe27730dccef1f47a9e4e7ee67326b4622188 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Thu, 22 Sep 2016 17:57:07 -0700 Subject: [PATCH 059/120] Converted to unbuffered channel, and removed comment about server testlets (opened issue 92 for the latter) Signed-off-by: Matt Oswalt --- agent/tasks/executetestrun.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/agent/tasks/executetestrun.go b/agent/tasks/executetestrun.go index d3cf2be..a3bb9de 100644 --- a/agent/tasks/executetestrun.go +++ b/agent/tasks/executetestrun.go @@ -82,9 +82,6 @@ func (ett ExecuteTestRunTask) Run() error { } } - // TODO(mierdin): What about testlets running as servers (i.e. 'iperf -s')? Are we spinning up len(tr.Targets) - // number of those? - // Execute testlets against all targets asynchronously for i := range tr.Targets { @@ -102,11 +99,10 @@ func (ett ExecuteTestRunTask) Run() error { // Attach buffer to command cmd.Stdout = cmdOutput - // Execute collector + // Execute testlet cmd.Start() - // TODO(mierdin): Why is this a buffered channel? Is this necessary? - done := make(chan error, 1) + done := make(chan error) go func() { done <- cmd.Wait() }() From 9be0b0a8f9703b54936bb43606ef7cf92d376cde Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Thu, 22 Sep 2016 19:07:07 -0700 Subject: [PATCH 060/120] Added URL to issue for scheduled testruns Signed-off-by: Matt Oswalt --- agent/tasks/executetestrun.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/agent/tasks/executetestrun.go b/agent/tasks/executetestrun.go index a3bb9de..1d13e73 100644 --- a/agent/tasks/executetestrun.go +++ b/agent/tasks/executetestrun.go @@ -47,9 +47,10 @@ func (ett ExecuteTestRunTask) Run() error { var wg sync.WaitGroup // Waiting three seconds to ensure all the agents have their tasks before we potentially hammer the network + // // TODO(mierdin): This is a temporary measure - in the future, testruns will be executed via time schedule, // making not only this sleep, but also the entire task unnecessary. Testruns will simply be installed, and - // executed when the time is right. + // executed when the time is right. This is, in part tracked by https://github.com/Mierdin/todd/issues/89 time.Sleep(3000 * time.Millisecond) // Retrieve test from cache by UUID From 5aa328833e06445c89d0736b9a17aa5234eac8dc Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Thu, 22 Sep 2016 19:07:19 -0700 Subject: [PATCH 061/120] Removed unnecessary comment block Signed-off-by: Matt Oswalt --- agent/testing/testing.go | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/agent/testing/testing.go b/agent/testing/testing.go index 9155e9d..2485dda 100644 --- a/agent/testing/testing.go +++ b/agent/testing/testing.go @@ -42,22 +42,6 @@ type Testlet interface { Run(string, []string, int) (map[string]string, error) } -// NOTE -// -// Early efforts to build native-Go testlets involved the embedding of testlet logic into the -// ToDD agent itself. As a result, it was important to build some reusable infrastructure so that goroutines -// running testlet code within the agent could be controlled, and that new testlets could benefit from this -// infrastructure. -// -// Since then, the decision was made to keep testlets as their own separate binaries. -// -// These testlets are in their own repositories, and they do actually use some of the logic below, just not as meaningfully -// and comprehensively as they would have if they were baked in to the agent. The development standard for all "blessed" -// testlets will still ensure that they use this interface, so that if we decide to bake them into the agent in the future, -// they'll already conform. -// -// (The vast majority of this code was inspired by the database drivers implementation in the stdlib) - type rtfunc func(target string, args []string, timeout int) (map[string]string, error) type BaseTestlet struct { From 467b5525b670d31c032e15501fad1e4b9b4ad176 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Thu, 22 Sep 2016 19:25:49 -0700 Subject: [PATCH 062/120] Removed duplicate code that checks for native testlets and retrieves path Signed-off-by: Matt Oswalt --- agent/tasks/executetestrun.go | 31 +++++++++---------------------- agent/tasks/installtestrun.go | 30 +++++++----------------------- agent/testing/testing.go | 34 +++++++++++++++++++++++++++++----- 3 files changed, 45 insertions(+), 50 deletions(-) diff --git a/agent/tasks/executetestrun.go b/agent/tasks/executetestrun.go index 1d13e73..06c71cf 100644 --- a/agent/tasks/executetestrun.go +++ b/agent/tasks/executetestrun.go @@ -12,7 +12,6 @@ import ( "bytes" "encoding/json" "errors" - "fmt" "os" "os/exec" "sync" @@ -66,21 +65,9 @@ func (ett ExecuteTestRunTask) Run() error { // Specify size of wait group equal to number of targets wg.Add(len(tr.Targets)) - var testlet_path string - isNative, newTestletName := testing.IsNativeTestlet(tr.Testlet) - - // If we're running a native testlet, we want testlet_path to simply be the testlet name - // (since it is a requirement that the native-Go testlets are in the PATH) - // If the testlet is not native, we can get the full path. - if isNative { - testlet_path = newTestletName - } else { - // Generate path to testlet and make sure it exists. - testlet_path = fmt.Sprintf("%s/assets/testlets/%s", ett.Config.LocalResources.OptDir, tr.Testlet) - if _, err := os.Stat(testlet_path); os.IsNotExist(err) { - log.Errorf("Testlet %s does not exist on this agent", testlet_path) - return errors.New("Error executing testrun - testlet doesn't exist on this agent.") - } + testletPath, err := testing.GetTestletPath(tr.Testlet, ett.Config.LocalResources.OptDir) + if err != nil { + return err } // Execute testlets against all targets asynchronously @@ -92,8 +79,8 @@ func (ett ExecuteTestRunTask) Run() error { defer wg.Done() - log.Debugf("Full testlet command and args: '%s %s %s'", testlet_path, thisTarget, tr.Args) - cmd := exec.Command(testlet_path, thisTarget, tr.Args) + log.Debugf("Full testlet command and args: '%s %s %s'", testletPath, thisTarget, tr.Args) + cmd := exec.Command(testletPath, thisTarget, tr.Args) // Stdout buffer cmdOutput := &bytes.Buffer{} @@ -114,16 +101,16 @@ func (ett ExecuteTestRunTask) Run() error { select { case <-time.After(time.Duration(ett.TimeLimit) * time.Second): if err := cmd.Process.Kill(); err != nil { - log.Errorf("Failed to kill %s after timeout: %s", testlet_path, err) + log.Errorf("Failed to kill %s after timeout: %s", testletPath, err) } else { - log.Debug("Successfully killed ", testlet_path) + log.Debug("Successfully killed ", testletPath) } case err := <-done: if err != nil { - log.Errorf("Testlet %s completed with error '%s'", testlet_path, err) + log.Errorf("Testlet %s completed with error '%s'", testletPath, err) gatheredData[thisTarget] = "error" } else { - log.Debugf("Testlet %s completed without error", testlet_path) + log.Debugf("Testlet %s completed without error", testletPath) } } diff --git a/agent/tasks/installtestrun.go b/agent/tasks/installtestrun.go index ccfd711..683673f 100644 --- a/agent/tasks/installtestrun.go +++ b/agent/tasks/installtestrun.go @@ -11,8 +11,6 @@ package tasks import ( "bytes" "errors" - "fmt" - "os" "os/exec" "strings" @@ -43,29 +41,15 @@ func (itt InstallTestRunTask) Run() error { return errors.New("Testlet parameter for this testrun is null") } - var testlet_path string - // Determine if this is a native testlet - isNative, newName := testing.IsNativeTestlet(itt.Tr.Testlet) - - // If we're running a native testlet, we want testlet_path to simply be the testlet name - // (since it is a requirement that the native-Go testlets are in the PATH) - // If the testlet is not native, we can get the full path. - if isNative { - log.Infof("%s is a native testlet - installing testrun.", itt.Tr.Testlet) - testlet_path = newName - } else { - // Generate path to testlet and make sure it exists. - testlet_path = fmt.Sprintf("%s/assets/testlets/%s", itt.Config.LocalResources.OptDir, itt.Tr.Testlet) - if _, err := os.Stat(testlet_path); os.IsNotExist(err) { - log.Errorf("Testlet %s does not exist on this agent", itt.Tr.Testlet) - return errors.New("Error installing testrun - testlet doesn't exist on this agent.") - } + testletPath, err := testing.GetTestletPath(itt.Tr.Testlet, itt.Config.LocalResources.OptDir) + if err != nil { + return err } // Run the testlet in check mode to verify that everything is okay to run this test - log.Debug("Running testlet in check mode: ", testlet_path) - cmd := exec.Command(testlet_path, "check") + log.Debug("Running testlet in check mode: ", testletPath) + cmd := exec.Command(testletPath, "check") // Stdout buffer cmdOutput := &bytes.Buffer{} @@ -76,7 +60,7 @@ func (itt InstallTestRunTask) Run() error { // This is probably the best cross-platform way to see if check mode passed. if strings.Contains(string(cmdOutput.Bytes()), "Check mode PASSED") { - log.Debugf("Check mode for %s passed", testlet_path) + log.Debugf("Check mode for %s passed", testletPath) } else { log.Error("Testlet returned an error during check mode: ", string(cmdOutput.Bytes())) return errors.New("Testlet returned an error during check mode") @@ -84,7 +68,7 @@ func (itt InstallTestRunTask) Run() error { // Insert testrun into agent cache var ac = cache.NewAgentCache(itt.Config) - err := ac.InsertTestRun(itt.Tr) + err = ac.InsertTestRun(itt.Tr) if err != nil { log.Error(err) return errors.New("Problem installing test run into agent cache") diff --git a/agent/testing/testing.go b/agent/testing/testing.go index 2485dda..001a162 100644 --- a/agent/testing/testing.go +++ b/agent/testing/testing.go @@ -11,6 +11,14 @@ package testing +import ( + "errors" + "fmt" + "os" + + log "github.com/Sirupsen/logrus" +) + var ( // This map provides name redirection so that the native testlets can use names that don't @@ -52,11 +60,27 @@ type BaseTestlet struct { } // IsNativeTestlet polls the list of registered native testlets, and returns -// true if the referenced name exists -func IsNativeTestlet(name string) (bool, string) { - if _, ok := nativeTestlets[name]; ok { - return true, nativeTestlets[name] +// true if the referenced name exists. Also returns path to testlet (if native, just it's name) +// If we're running a native testlet, we want testlet_path to simply be the testlet name +// (since it is a requirement that the native-Go testlets are in the PATH) +// If the testlet is not native, we can get the full path. +// << DOCUMENT RETURNS AND ARGS >> +func GetTestletPath(testletName, optDir string) (string, error) { + + if _, ok := nativeTestlets[testletName]; ok { + log.Infof("%s is a native testlet", testletName) + return nativeTestlets[testletName], nil } else { - return false, "" + + log.Infof("%s is a custom testlet", testletName) + + // Generate path to testlet and make sure it exists. + testletPath := fmt.Sprintf("%s/assets/testlets/%s", optDir, testletName) + if _, err := os.Stat(testletPath); os.IsNotExist(err) { + log.Errorf("Testlet %s does not exist on this agent", testletName) + return "", errors.New("Error installing testrun - testlet doesn't exist on this agent.") + } + + return testletPath, nil } } From 30a312db37e2e17e8d3ae2a28281dcd118f79d6c Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Thu, 22 Sep 2016 19:30:03 -0700 Subject: [PATCH 063/120] Updated docstring for GetTestletPath Signed-off-by: Matt Oswalt --- agent/testing/testing.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/agent/testing/testing.go b/agent/testing/testing.go index 001a162..9881dc7 100644 --- a/agent/testing/testing.go +++ b/agent/testing/testing.go @@ -59,12 +59,11 @@ type BaseTestlet struct { RunFunction rtfunc } -// IsNativeTestlet polls the list of registered native testlets, and returns -// true if the referenced name exists. Also returns path to testlet (if native, just it's name) -// If we're running a native testlet, we want testlet_path to simply be the testlet name -// (since it is a requirement that the native-Go testlets are in the PATH) -// If the testlet is not native, we can get the full path. -// << DOCUMENT RETURNS AND ARGS >> +// GetTestletPath generates whatever path is needed to reach the given testlet +// It first determines if the referenced testlet is native or not - if it is native, +// then only the name needs to be returned (all native testlets must be in the path). +// If it is a custom testlet, then it will generate the full path to the testlet, +// ensure it is a valid path, and if so, return that full path back to the caller. func GetTestletPath(testletName, optDir string) (string, error) { if _, ok := nativeTestlets[testletName]; ok { From 06fb8549c81195588d1de406cd8dbe1fcd88b341 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Thu, 22 Sep 2016 19:35:33 -0700 Subject: [PATCH 064/120] Removed roadmap (will be re-added in upcoming docs revamp Signed-off-by: Matt Oswalt --- docs/roadmap.rst | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 docs/roadmap.rst diff --git a/docs/roadmap.rst b/docs/roadmap.rst deleted file mode 100644 index 9c9be29..0000000 --- a/docs/roadmap.rst +++ /dev/null @@ -1,17 +0,0 @@ -Roadmap -================================ - -Goals for Alpha Release - -* Store group-to-group aggreagate test data as a graph (agent groups are the lowest addressable node in teh graph) -* Consider getting rid of YAML files. It should be easy to run tests using a one-liner (native testlets should have reasonable defaults) or they just run on their own in the background -* Simplify agent setup. Is there a way to provide configuration-less agent setup? -* Need to look at modifying ToDD to run tests in a hands-off way. Think about the inspiration from the vendors at NFD12. Need to rethink the execution of tests. You know you can store test results without server connectivity, but can you run tests without server connectivity? - -Goals for Beta Release - -* Goal 1 - -Goals for Full Release - -* Web Front-End? \ No newline at end of file From 1115f6dae25afaee4dd22ce49705c736310a1f50 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Thu, 22 Sep 2016 19:36:07 -0700 Subject: [PATCH 065/120] Removed old DSL files Signed-off-by: Matt Oswalt --- docs/dsl/branch-to-dc-bw.yml | 14 -------------- docs/dsl/group-branch.yml | 7 ------- docs/dsl/group-uraj.yml | 7 ------- 3 files changed, 28 deletions(-) delete mode 100644 docs/dsl/branch-to-dc-bw.yml delete mode 100644 docs/dsl/group-branch.yml delete mode 100644 docs/dsl/group-uraj.yml diff --git a/docs/dsl/branch-to-dc-bw.yml b/docs/dsl/branch-to-dc-bw.yml deleted file mode 100644 index b1683a4..0000000 --- a/docs/dsl/branch-to-dc-bw.yml +++ /dev/null @@ -1,14 +0,0 @@ ---- -# Example test file -type: testrun -label: branch-to-dc-bw -spec: - targettype: group - source: - name: branch-uraj - app: iperf - args: "-c {{ target }}" - target: - name: datacenter - app: iperf - args: "-s" diff --git a/docs/dsl/group-branch.yml b/docs/dsl/group-branch.yml deleted file mode 100644 index b1d1e82..0000000 --- a/docs/dsl/group-branch.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -type: group -label: branch-uraj -spec: - group: branch-uraj - matches: - - hostname: "uraj" diff --git a/docs/dsl/group-uraj.yml b/docs/dsl/group-uraj.yml deleted file mode 100644 index b1d1e82..0000000 --- a/docs/dsl/group-uraj.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -type: group -label: branch-uraj -spec: - group: branch-uraj - matches: - - hostname: "uraj" From a6e5a7d2050d25aa686eea5d59e671d7d9ba1118 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Thu, 22 Sep 2016 19:36:25 -0700 Subject: [PATCH 066/120] Used correct loglevel in GetTestletPath Signed-off-by: Matt Oswalt --- agent/testing/testing.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agent/testing/testing.go b/agent/testing/testing.go index 9881dc7..539ad21 100644 --- a/agent/testing/testing.go +++ b/agent/testing/testing.go @@ -67,11 +67,11 @@ type BaseTestlet struct { func GetTestletPath(testletName, optDir string) (string, error) { if _, ok := nativeTestlets[testletName]; ok { - log.Infof("%s is a native testlet", testletName) + log.Debugf("%s is a native testlet", testletName) return nativeTestlets[testletName], nil } else { - log.Infof("%s is a custom testlet", testletName) + log.Debugf("%s is a custom testlet", testletName) // Generate path to testlet and make sure it exists. testletPath := fmt.Sprintf("%s/assets/testlets/%s", optDir, testletName) From 1b91cf91fa0347961f4ffe9dc8b9113f559683eb Mon Sep 17 00:00:00 2001 From: Kale Blankenship Date: Fri, 23 Sep 2016 12:11:16 -0700 Subject: [PATCH 067/120] Add error handling to influxdb TSDB plugin (#79) Should help in resolving #76 --- server/tsdb/influxdb.go | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/server/tsdb/influxdb.go b/server/tsdb/influxdb.go index a57acc5..1474d86 100644 --- a/server/tsdb/influxdb.go +++ b/server/tsdb/influxdb.go @@ -46,10 +46,14 @@ func (ifdb influxDB) WriteData(testUuid, testRunName, groupName string, testData defer c.Close() // Create a new point batch - bp, _ := influx.NewBatchPoints(influx.BatchPointsConfig{ + bp, err := influx.NewBatchPoints(influx.BatchPointsConfig{ Database: ifdb.config.TSDB.DatabaseName, Precision: "s", }) + if err != nil { + log.Error("Error creating InfluxDB Batch Points: ", err) + return err + } // Need to publish data from all of the agents that took part in this test for agentUuid, agentData := range testData { @@ -68,12 +72,20 @@ func (ifdb influxDB) WriteData(testUuid, testRunName, groupName string, testData // Convert our metrics to float and insert into influx fields fields := make(map[string]interface{}) for k, v := range metrics { - float_v, _ := strconv.ParseFloat(v, 64) + float_v, err := strconv.ParseFloat(v, 64) + if err != nil { + log.Errorf("Error parsing metric value from %q, %q=%q: %v\n", targetAddress, k, v, err) + return err + } fields[k] = float_v } - pt, _ := influx.NewPoint(fmt.Sprintf("testrun-%s", testRunName), tags, fields, time.Now()) - bp.AddPoint(pt) + pt, err := influx.NewPoint(fmt.Sprintf("testrun-%s", testRunName), tags, fields, time.Now()) + if err != nil { + log.Error("Error creating InfluxDB Point: ", err) + return err + } + bp.AddPoint(pt) } } @@ -81,7 +93,7 @@ func (ifdb influxDB) WriteData(testUuid, testRunName, groupName string, testData // Write the batch err = c.Write(bp) if err != nil { - log.Fatal(err) + log.Error("Error writing InfluxDB Batch Points: ", err) return err } From 79d91f61390db8dcbee7d02374c9b4b8f906111f Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Tue, 19 Jul 2016 01:35:36 -0700 Subject: [PATCH 068/120] Saving progress on building native testlet infrastructure Signed-off-by: Matt Oswalt --- agent/tasks/executetestrun.go | 117 +++++++++++++++---------- agent/tasks/installtestrun.go | 56 +++++++----- agent/testing/testlets/{ping => _ping} | 0 agent/testing/testlets/ping/ping.go | 46 ++++++++++ agent/testing/testlets/testlets.go | 74 +++++++++++++++- cmd/todd-agent/main.go | 6 +- 6 files changed, 229 insertions(+), 70 deletions(-) rename agent/testing/testlets/{ping => _ping} (100%) create mode 100644 agent/testing/testlets/ping/ping.go diff --git a/agent/tasks/executetestrun.go b/agent/tasks/executetestrun.go index 6ed6e48..09904b0 100644 --- a/agent/tasks/executetestrun.go +++ b/agent/tasks/executetestrun.go @@ -21,9 +21,20 @@ import ( log "github.com/Sirupsen/logrus" "github.com/Mierdin/todd/agent/cache" + "github.com/Mierdin/todd/agent/testing/testlets" "github.com/Mierdin/todd/config" ) +var ( + // gatheredData represents test data from this agent for all targets. + // Key is target name, value is JSON output from testlet for that target + // This is reset to a blank map every time ExecuteTestRunTask is called + gatheredData = make(map[string]string) + + // Use a wait group to ensure that all of the testlets have a chance to finish + wg sync.WaitGroup +) + // ExecuteTestRunTask defines this particular task. type ExecuteTestRunTask struct { BaseTask @@ -37,6 +48,9 @@ type ExecuteTestRunTask struct { // a testrun will be executed once per target, all in parallel. func (ett ExecuteTestRunTask) Run() error { + // Make sure we're working with a clean slate + gatheredData = map[string]string{} + // Waiting three seconds to ensure all the agents have their tasks before we potentially hammer the network // TODO(mierdin): This is a bit of a copout. I would like to do something a little more robust than simply waiting // for a few seconds in the future. @@ -50,70 +64,79 @@ func (ett ExecuteTestRunTask) Run() error { return errors.New("Problem retrieving testrun from agent cache") } - // Generate path to testlet and make sure it exists. - testlet_path := fmt.Sprintf("%s/assets/testlets/%s", ett.Config.LocalResources.OptDir, tr.Testlet) - if _, err := os.Stat(testlet_path); os.IsNotExist(err) { - log.Errorf("Testlet %s does not exist on this agent", tr.Testlet) - return errors.New("Error executing testrun - testlet doesn't exist on this agent.") - } - log.Debugf("IMMA FIRIN MAH LAZER (for test %s) ", ett.TestUuid) - // Use a wait group to ensure that all of the testlets have a chance to finish - var wg sync.WaitGroup + // Specify size of wait group wg.Add(len(tr.Targets)) - // gatheredData represents test data from this agent for all targets. - // Key is target name, value is JSON output from testlet for that target - gatheredData := make(map[string]string) + // Determine if this is a native testlet + nativeTestlet, err := testlets.NewTestlet(tr.Testlet) + native := (err == nil) // Execute testlets against all targets asynchronously for i := range tr.Targets { thisTarget := tr.Targets[i] - go func() { - defer wg.Done() - - log.Debugf("Full testlet command and args: '%s %s %s'", testlet_path, thisTarget, tr.Args) - cmd := exec.Command(testlet_path, thisTarget, tr.Args) - - // Stdout buffer - cmdOutput := &bytes.Buffer{} - // Attach buffer to command - cmd.Stdout = cmdOutput + if native { + go func() { - // Execute collector - cmd.Start() + // TODO(mierdin): Just calling a temporary method to ensure we can get to the native testlet. + // (it works!) + log.Error(nativeTestlet.Test()) + }() + } else { + // Generate path to testlet and make sure it exists. + testlet_path := fmt.Sprintf("%s/assets/testlets/%s", ett.Config.LocalResources.OptDir, tr.Testlet) + if _, err := os.Stat(testlet_path); os.IsNotExist(err) { + log.Errorf("Testlet %s does not exist on this agent", tr.Testlet) + return errors.New("Error executing testrun - testlet doesn't exist on this agent.") + } - done := make(chan error, 1) go func() { - done <- cmd.Wait() - }() - // This select statement will block until one of these two conditions are met: - // - The testlet finishes, in which case the channel "done" will be receive a value - // - The configured time limit is exceeded (expected for testlets running in server mode) - select { - case <-time.After(time.Duration(ett.TimeLimit) * time.Second): - if err := cmd.Process.Kill(); err != nil { - log.Errorf("Failed to kill %s after timeout: %s", testlet_path, err) - } else { - log.Debug("Successfully killed ", testlet_path) - } - case err := <-done: - if err != nil { - log.Errorf("Testlet %s completed with error '%s'", testlet_path, err) - gatheredData[thisTarget] = "error" - } else { - log.Debugf("Testlet %s completed without error", testlet_path) + defer wg.Done() + + log.Debugf("Full testlet command and args: '%s %s %s'", testlet_path, thisTarget, tr.Args) + cmd := exec.Command(testlet_path, thisTarget, tr.Args) + + // Stdout buffer + cmdOutput := &bytes.Buffer{} + // Attach buffer to command + cmd.Stdout = cmdOutput + + // Execute collector + cmd.Start() + + done := make(chan error, 1) + go func() { + done <- cmd.Wait() + }() + + // This select statement will block until one of these two conditions are met: + // - The testlet finishes, in which case the channel "done" will be receive a value + // - The configured time limit is exceeded (expected for testlets running in server mode) + select { + case <-time.After(time.Duration(ett.TimeLimit) * time.Second): + if err := cmd.Process.Kill(); err != nil { + log.Errorf("Failed to kill %s after timeout: %s", testlet_path, err) + } else { + log.Debug("Successfully killed ", testlet_path) + } + case err := <-done: + if err != nil { + log.Errorf("Testlet %s completed with error '%s'", testlet_path, err) + gatheredData[thisTarget] = "error" + } else { + log.Debugf("Testlet %s completed without error", testlet_path) + } } - } - // Record test data - gatheredData[thisTarget] = string(cmdOutput.Bytes()) + // Record test data + gatheredData[thisTarget] = string(cmdOutput.Bytes()) - }() + }() + } } wg.Wait() diff --git a/agent/tasks/installtestrun.go b/agent/tasks/installtestrun.go index c45da42..131df4d 100644 --- a/agent/tasks/installtestrun.go +++ b/agent/tasks/installtestrun.go @@ -20,6 +20,7 @@ import ( "github.com/Mierdin/todd/agent/cache" "github.com/Mierdin/todd/agent/defs" + "github.com/Mierdin/todd/agent/testing/testlets" "github.com/Mierdin/todd/config" ) @@ -42,35 +43,48 @@ func (itt InstallTestRunTask) Run() error { return errors.New("Testlet parameter for this testrun is null") } - // Generate path to testlet and make sure it exists. - testlet_path := fmt.Sprintf("%s/assets/testlets/%s", itt.Config.LocalResources.OptDir, itt.Tr.Testlet) - if _, err := os.Stat(testlet_path); os.IsNotExist(err) { - log.Errorf("Testlet %s does not exist on this agent", itt.Tr.Testlet) - return errors.New("Error installing testrun - testlet doesn't exist on this agent.") - } + // Determine if this is a valid native testlet + _, err := testlets.NewTestlet(itt.Tr.Testlet) + + // Not a native testlet - attempt to run check mode on testlet in filesystem + if err != nil { + + // Generate path to testlet and make sure it exists. + testlet_path := fmt.Sprintf("%s/assets/testlets/%s", itt.Config.LocalResources.OptDir, itt.Tr.Testlet) + if _, err := os.Stat(testlet_path); os.IsNotExist(err) { + log.Errorf("Testlet %s does not exist on this agent", itt.Tr.Testlet) + return errors.New("Error installing testrun - testlet doesn't exist on this agent.") + } - // Run the testlet in check mode to verify that everything is okay to run this test - log.Debug("Running testlet in check mode: ", testlet_path) - cmd := exec.Command(testlet_path, "check") + // Run the testlet in check mode to verify that everything is okay to run this test + log.Debug("Running testlet in check mode: ", testlet_path) + cmd := exec.Command(testlet_path, "check") - // Stdout buffer - cmdOutput := &bytes.Buffer{} - // Attach buffer to command - cmd.Stdout = cmdOutput - // Execute collector - cmd.Run() + // Stdout buffer + cmdOutput := &bytes.Buffer{} + // Attach buffer to command + cmd.Stdout = cmdOutput + // Execute collector + cmd.Run() + + // This is probably the best cross-platform way to see if check mode passed. + if strings.Contains(string(cmdOutput.Bytes()), "Check mode PASSED") { + log.Debugf("Check mode for %s passed", testlet_path) + } else { + log.Error("Testlet returned an error during check mode: ", string(cmdOutput.Bytes())) + return errors.New("Testlet returned an error during check mode") + } - // This is probably the best cross-platform way to see if check mode passed. - if strings.Contains(string(cmdOutput.Bytes()), "Check mode PASSED") { - log.Debugf("Check mode for %s passed", testlet_path) } else { - log.Error("Testlet returned an error during check mode: ", string(cmdOutput.Bytes())) - return errors.New("Testlet returned an error during check mode") + + // Nothing to do, as we're using a native testlet + log.Infof("%s is a native testlet - installing testrun.", itt.Tr.Testlet) + } // Insert testrun into agent cache var ac = cache.NewAgentCache(itt.Config) - err := ac.InsertTestRun(itt.Tr) + err = ac.InsertTestRun(itt.Tr) if err != nil { log.Error(err) return errors.New("Problem installing test run into agent cache") diff --git a/agent/testing/testlets/ping b/agent/testing/testlets/_ping similarity index 100% rename from agent/testing/testlets/ping rename to agent/testing/testlets/_ping diff --git a/agent/testing/testlets/ping/ping.go b/agent/testing/testlets/ping/ping.go new file mode 100644 index 0000000..f68b43b --- /dev/null +++ b/agent/testing/testlets/ping/ping.go @@ -0,0 +1,46 @@ +package toddping + +// NOTE ////////////////// +// +// This is a built-in testlet. Currently, the approach is to have each +// testlet under it's own package, which is explicitly imported under +// the ToDD agent's 'main' package. +// +// Currently, each testlet is stored within the ToDD repo in order to +// vet out the architecture, which means they are awkwardly placed +// underneath the "testlets" directory together. However, this should be +// a tempoarary holding place, as the main effort around native testlets +// is being implemented so they can be broken out into their own repos. + +import ( + "github.com/Mierdin/todd/agent/testing/testlets" +) + +type PingTestlet struct{} + +func init() { + testlets.Register("ping", &PingTestlet{}) +} + +// TODO(mierdin): Maybe consider running these asyc by default? Basically +// the "Run" function kicks back a channel of type map[string]string so when +// it's populated, it contains the metrics and you know it can stop + +func (p PingTestlet) Run(target string, args []string) (map[string]string, error) { + + // TODO(mierdin): Implement ping test here + + return map[string]string{}, nil +} + +func (p PingTestlet) Kill() error { + // TODO (mierdin): This will have to be coordinated with the task above. Basically + // you need a way to kill this testlet (and that's really only possible when running + // async) + + return nil +} + +func (p PingTestlet) Test() string { + return "trolololol" +} diff --git a/agent/testing/testlets/testlets.go b/agent/testing/testlets/testlets.go index fc84175..6ad65cf 100644 --- a/agent/testing/testlets/testlets.go +++ b/agent/testing/testlets/testlets.go @@ -1,7 +1,79 @@ package testlets +import ( + "errors" + "fmt" + "sort" + "sync" + //"sync/atomic" +) + +var ( + testletsMu sync.RWMutex + testlets = make(map[string]Testlet) +) + // Testlet defines what a testlet should look like if built in native // go and compiled with the agent type Testlet interface { - Run(string, []string) ([]byte, error) + + // Params are + // target (string) + // args ([]string) + // + // Returns: + // metrics (map[string]interface{}) + // (name of metric is key, value is metric value) + Run(string, []string) (map[string]string, error) + + Kill() error + + Test() string //TODO(mierdin): Remove me +} + +//NewTestlet produces a new testlet based on the "name" param +func NewTestlet(name string) (Testlet, error) { + + if testlet, ok := testlets[name]; ok { + return testlet, nil + } else { + return nil, errors.New( + fmt.Sprintf("'%s' not currently supported as a native testlet"), + ) + } +} + +// Register makes a testlet available by the provided name. +// If Register is called twice with the same name or if testlet is nil, +// it will return an error +func Register(name string, testlet Testlet) error { + testletsMu.Lock() + defer testletsMu.Unlock() + if testlet == nil { + return errors.New("Register testlet is nil") + } + if _, dup := testlets[name]; dup { + return errors.New("Register called twice for testlet " + name) + } + testlets[name] = testlet + return nil +} + +func unregisterAllDrivers() { + testletsMu.Lock() + defer testletsMu.Unlock() + // For tests. + testlets = make(map[string]Testlet) +} + +// Testlets returns a sorted list of the names of the registered testlets. +func Testlets() []string { + testletsMu.RLock() + defer testletsMu.RUnlock() + var list []string + for name := range testlets { + list = append(list, name) + } + sort.Strings(list) + return list } diff --git a/cmd/todd-agent/main.go b/cmd/todd-agent/main.go index 51f75ad..30950d9 100644 --- a/cmd/todd-agent/main.go +++ b/cmd/todd-agent/main.go @@ -14,6 +14,8 @@ import ( "os" "time" + log "github.com/Sirupsen/logrus" + "github.com/Mierdin/todd/agent/cache" "github.com/Mierdin/todd/agent/defs" "github.com/Mierdin/todd/agent/facts" @@ -21,7 +23,9 @@ import ( "github.com/Mierdin/todd/comms" "github.com/Mierdin/todd/config" "github.com/Mierdin/todd/hostresources" - log "github.com/Sirupsen/logrus" + + // Testlet imports (importing these packages registers the testlets) + _ "github.com/Mierdin/todd/agent/testing/testlets/ping" ) // Command-line Arguments From 9ce584482e4b12ebbab24594a1b175c54d8b3ff7 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Tue, 19 Jul 2016 01:36:05 -0700 Subject: [PATCH 069/120] Docs update Signed-off-by: Matt Oswalt --- docs/nativetests.rst | 6 ++++++ docs/testlets.rst | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 docs/nativetests.rst diff --git a/docs/nativetests.rst b/docs/nativetests.rst new file mode 100644 index 0000000..e6c04a3 --- /dev/null +++ b/docs/nativetests.rst @@ -0,0 +1,6 @@ +Native Tests +================================ + +Need to talk about the native tests you've built in, and turn the "testlets" doc into more of a "so you want to build your own, eh?" + +Also need to figure out if you want to refer to both native and non-native as "testlets", or maybe reserve that for non-native \ No newline at end of file diff --git a/docs/testlets.rst b/docs/testlets.rst index fab1508..06afe4d 100644 --- a/docs/testlets.rst +++ b/docs/testlets.rst @@ -18,7 +18,7 @@ When you want to run a certain testlet, you refer to it by name. There are a num * bandwidth * ping -You can, of course, build your own testlet (provided it follows the standard defined on this page) and refer to it by it's filename. +If you don't want to use any of the built-in testlets, you can, of course, build your own testlet (provided it follows the standard defined on this page) and refer to it by it's filename. Check Mode ---------- From 6b6fe19398301cee0f60a8c4180e22d3b9d39313 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Thu, 28 Jul 2016 01:45:12 -0700 Subject: [PATCH 070/120] Made progress on a native testlet implementation Signed-off-by: Matt Oswalt --- agent/tasks/executetestrun.go | 33 ++++++++++++++++++++++------- agent/testing/testlets/ping/ping.go | 21 +++++++++--------- agent/testing/testlets/testlets.go | 10 ++++++++- 3 files changed, 45 insertions(+), 19 deletions(-) diff --git a/agent/tasks/executetestrun.go b/agent/tasks/executetestrun.go index 09904b0..c72ccb1 100644 --- a/agent/tasks/executetestrun.go +++ b/agent/tasks/executetestrun.go @@ -69,21 +69,38 @@ func (ett ExecuteTestRunTask) Run() error { // Specify size of wait group wg.Add(len(tr.Targets)) - // Determine if this is a native testlet - nativeTestlet, err := testlets.NewTestlet(tr.Testlet) - native := (err == nil) - // Execute testlets against all targets asynchronously for i := range tr.Targets { thisTarget := tr.Targets[i] - if native { + if testlets.IsNativeTestlet(tr.Testlet) { go func() { - // TODO(mierdin): Just calling a temporary method to ensure we can get to the native testlet. - // (it works!) - log.Error(nativeTestlet.Test()) + // TODO(mierdin): Something worried me here (can't remember what) regarding + // if only some agents were running native testlets, does this wait group methodology work? + // Sorry it's not clear, I have had a bit too much wine. + defer wg.Done() + + nativeTestlet, err := testlets.NewTestlet(tr.Testlet) + if err != nil { + //TODO(mierdin) do something + } + + metrics, err := nativeTestlet.Run("8.8.8.8", []string{"-c 10", "-s"}) + if err != nil { + //TODO(mierdin) do something + } + + // The metrics infrastructure requires that we collect metrics as a JSON string + // (which is a result of building non-native testlets in early versions of ToDD) + // So let's convert, and add to gatheredData + metrics_json, err := json.Marshal(metrics) + if err != nil { + //TODO(mierdin) do something + } + gatheredData[thisTarget] = string(metrics_json) + }() } else { // Generate path to testlet and make sure it exists. diff --git a/agent/testing/testlets/ping/ping.go b/agent/testing/testlets/ping/ping.go index f68b43b..51f29e6 100644 --- a/agent/testing/testlets/ping/ping.go +++ b/agent/testing/testlets/ping/ping.go @@ -13,24 +13,29 @@ package toddping // is being implemented so they can be broken out into their own repos. import ( + "time" + "github.com/Mierdin/todd/agent/testing/testlets" ) type PingTestlet struct{} func init() { + + // This is important - register the name of this testlet + // (the name the user will use in a testrun definition) testlets.Register("ping", &PingTestlet{}) } -// TODO(mierdin): Maybe consider running these asyc by default? Basically -// the "Run" function kicks back a channel of type map[string]string so when -// it's populated, it contains the metrics and you know it can stop - func (p PingTestlet) Run(target string, args []string) (map[string]string, error) { - // TODO(mierdin): Implement ping test here + // TODO(mierdin): Implement ping test here - this is just a mock + time.Sleep(3000 * time.Millisecond) + return map[string]string{ + "avg_latency_ms": "25.144", + "packet_loss_percentage": "0", + }, nil - return map[string]string{}, nil } func (p PingTestlet) Kill() error { @@ -40,7 +45,3 @@ func (p PingTestlet) Kill() error { return nil } - -func (p PingTestlet) Test() string { - return "trolololol" -} diff --git a/agent/testing/testlets/testlets.go b/agent/testing/testlets/testlets.go index 6ad65cf..64ed586 100644 --- a/agent/testing/testlets/testlets.go +++ b/agent/testing/testlets/testlets.go @@ -27,8 +27,16 @@ type Testlet interface { Run(string, []string) (map[string]string, error) Kill() error +} - Test() string //TODO(mierdin): Remove me +// IsNativeTestlet polls the list of registered native testlets, and returns +// true if the referenced name exists +func IsNativeTestlet(name string) bool { + if _, ok := testlets[name]; ok { + return true + } else { + return false + } } //NewTestlet produces a new testlet based on the "name" param From e9f72e82b1d015d72cb41cc234ad773afa9b52e2 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Fri, 29 Jul 2016 02:21:01 -0700 Subject: [PATCH 071/120] Implement async handling of testlet running Signed-off-by: Matt Oswalt --- agent/tasks/executetestrun.go | 7 ++- agent/testing/testlets/ping/ping.go | 29 ++++++----- agent/testing/testlets/testlets.go | 81 ++++++++++++++++++++++++++++- 3 files changed, 101 insertions(+), 16 deletions(-) diff --git a/agent/tasks/executetestrun.go b/agent/tasks/executetestrun.go index c72ccb1..b9210de 100644 --- a/agent/tasks/executetestrun.go +++ b/agent/tasks/executetestrun.go @@ -87,9 +87,11 @@ func (ett ExecuteTestRunTask) Run() error { //TODO(mierdin) do something } - metrics, err := nativeTestlet.Run("8.8.8.8", []string{"-c 10", "-s"}) + metrics, err := nativeTestlet.Run("8.8.8.8", []string{"-c 10", "-s"}, ett.TimeLimit) + //log.Error(nativeTestlet.RunFunction) if err != nil { - //TODO(mierdin) do something + log.Errorf("Testlet completed with error '%s'", err) + gatheredData[thisTarget] = "error" } // The metrics infrastructure requires that we collect metrics as a JSON string @@ -125,6 +127,7 @@ func (ett ExecuteTestRunTask) Run() error { // Execute collector cmd.Start() + // TODO(mierdin): Does this need to be a buffered channel? done := make(chan error, 1) go func() { done <- cmd.Wait() diff --git a/agent/testing/testlets/ping/ping.go b/agent/testing/testlets/ping/ping.go index 51f29e6..8ceb887 100644 --- a/agent/testing/testlets/ping/ping.go +++ b/agent/testing/testlets/ping/ping.go @@ -9,25 +9,38 @@ package toddping // Currently, each testlet is stored within the ToDD repo in order to // vet out the architecture, which means they are awkwardly placed // underneath the "testlets" directory together. However, this should be -// a tempoarary holding place, as the main effort around native testlets +// a temporary holding place, as the main effort around native testlets // is being implemented so they can be broken out into their own repos. import ( "time" + // log "github.com/Sirupsen/logrus" + "github.com/Mierdin/todd/agent/testing/testlets" ) -type PingTestlet struct{} +type PingTestlet struct { + testlets.BaseTestlet +} func init() { + var pt = PingTestlet{} + + // Ensure the RunFunction attribute is set correctly. + // This allows the underlying testlet infrastructure + // to know what function to call at runtime + pt.RunFunction = pt.RunTestlet + // This is important - register the name of this testlet // (the name the user will use in a testrun definition) - testlets.Register("ping", &PingTestlet{}) + testlets.Register("ping", &pt) } -func (p PingTestlet) Run(target string, args []string) (map[string]string, error) { +// RunTestlet implements the core logic of the testlet. Don't worry about running asynchronously, +// that's handled by the infrastructure. +func (p PingTestlet) RunTestlet(target string, args []string) (map[string]string, error) { // TODO(mierdin): Implement ping test here - this is just a mock time.Sleep(3000 * time.Millisecond) @@ -37,11 +50,3 @@ func (p PingTestlet) Run(target string, args []string) (map[string]string, error }, nil } - -func (p PingTestlet) Kill() error { - // TODO (mierdin): This will have to be coordinated with the task above. Basically - // you need a way to kill this testlet (and that's really only possible when running - // async) - - return nil -} diff --git a/agent/testing/testlets/testlets.go b/agent/testing/testlets/testlets.go index 64ed586..0ebb0c7 100644 --- a/agent/testing/testlets/testlets.go +++ b/agent/testing/testlets/testlets.go @@ -5,30 +5,104 @@ import ( "fmt" "sort" "sync" + "time" //"sync/atomic" + + log "github.com/Sirupsen/logrus" ) var ( testletsMu sync.RWMutex testlets = make(map[string]Testlet) + done = make(chan error) ) // Testlet defines what a testlet should look like if built in native // go and compiled with the agent type Testlet interface { + // Run is the "workflow" function for a testlet. It handles running + // the RunTestlet function asynchronously and managing the state therein. + // // Params are // target (string) // args ([]string) + // timeLimit (int in seconds) // // Returns: // metrics (map[string]interface{}) // (name of metric is key, value is metric value) - Run(string, []string) (map[string]string, error) + // + // Keep as much logic out of here as possible. All native testlets + // must support a "Kill" method, so it's best to implement core testlet + // logic in a separate function so that the Run and Kill commands can manage + // execution of that logic in a goroutine + Run(string, []string, int) (map[string]string, error) + // RunTestlet is designed to be the one-stop shop for testlet logic. + // The developer of a native testlet just needs to implement the testlet logic here, + // without worrying about things like managing goroutines or channels. That's all + // managed by the "Run" or "Kill" functions + RunTestlet(string, []string) (map[string]string, error) + // TODO(mierdin): is this really the best name for it? Maybe something that's less confusing, less like "Run" + + // All testlets must be able to stop operation when sent a Kill command. Kill() error } +type rtfunc func(target string, args []string) (map[string]string, error) + +type BaseTestlet struct { + + // rtfunc is a type that will store our RunTestlet function. It is the responsibility + // of the "child" testlet to set this value upon creation + RunFunction rtfunc +} + +// Run takes care of running the testlet function and managing it's operation given the parameters provided +func (b BaseTestlet) Run(target string, args []string, timeLimit int) (map[string]string, error) { + + var metrics map[string]string + + // TODO(mierdin): ensure channel is nil + // done = make(chan error) + + // TODO(mierdin): Based on experimentation, this will keep running even if this function returns. + // Need to be sure about how this ends. Also might want to evaluate the same for the existing non-native model, likely has the same issue + go func() { + theseMetrics, err := b.RunFunction(target, args) + metrics = theseMetrics //TODO(mierdin): Gross. + done <- err + }() + + // This select statement will block until one of these two conditions are met: + // - The testlet finishes, in which case the channel "done" will be receive a value + // - The configured time limit is exceeded (expected for testlets running in server mode) + select { + case <-time.After(time.Duration(timeLimit) * time.Second): + log.Debug("Successfully killed ") + return map[string]string{}, nil + + case err := <-done: + if err != nil { + return map[string]string{}, errors.New("testlet error") // TODO(mierdin): elaborate? + } else { + log.Debugf("Testlet completed without error") + return metrics, nil + } + } +} + +func (b BaseTestlet) Kill() error { + // TODO (mierdin): This will have to be coordinated with the task above. Basically + // you need a way to kill this testlet (and that's really only possible when running + // async) + + // Probably just want to set the channel to something so the select within "Run" will execute + + return nil +} + // IsNativeTestlet polls the list of registered native testlets, and returns // true if the referenced name exists func IsNativeTestlet(name string) bool { @@ -43,6 +117,9 @@ func IsNativeTestlet(name string) bool { func NewTestlet(name string) (Testlet, error) { if testlet, ok := testlets[name]; ok { + + // testlet.runFunction = testlet.run + return testlet, nil } else { return nil, errors.New( @@ -67,7 +144,7 @@ func Register(name string, testlet Testlet) error { return nil } -func unregisterAllDrivers() { +func unregisterAllTestlets() { testletsMu.Lock() defer testletsMu.Unlock() // For tests. From 1e6fdf46792c7f26332e74a71756c1daaf470e7f Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Sun, 31 Jul 2016 16:30:10 -0700 Subject: [PATCH 072/120] Further progress Signed-off-by: Matt Oswalt --- agent/testing/testlets/ping/ping.go | 57 ++++++++++++++++++++++++++--- agent/testing/testlets/testlets.go | 10 +++-- 2 files changed, 57 insertions(+), 10 deletions(-) diff --git a/agent/testing/testlets/ping/ping.go b/agent/testing/testlets/ping/ping.go index 8ceb887..42cc8d3 100644 --- a/agent/testing/testlets/ping/ping.go +++ b/agent/testing/testlets/ping/ping.go @@ -13,9 +13,10 @@ package toddping // is being implemented so they can be broken out into their own repos. import ( + "fmt" "time" - // log "github.com/Sirupsen/logrus" + log "github.com/Sirupsen/logrus" "github.com/Mierdin/todd/agent/testing/testlets" ) @@ -40,13 +41,57 @@ func init() { // RunTestlet implements the core logic of the testlet. Don't worry about running asynchronously, // that's handled by the infrastructure. -func (p PingTestlet) RunTestlet(target string, args []string) (map[string]string, error) { +func (p PingTestlet) RunTestlet(target string, args []string, kill chan (bool)) (map[string]string, error) { + + // Get number of pings + count := 3 //TODO(mierdin): need to parse from 'args', or if omitted, use a default value + + log.Error(args) + + var latencies []float32 + var replies int + + // Execute ping once per count + i := 0 + for i < count { + select { + case <-kill: + // Terminating early; return empty metrics + return map[string]string{}, nil + default: + + //log.Debugf("Executing ping #%d", i) + + // Mocked ping logic + latency, replyReceived := pingTemp(count) + + latencies = append(latencies, latency) + + if replyReceived { + replies += 1 + } + + i += 1 + time.Sleep(1000 * time.Millisecond) + + } + } + + // Calculate metrics + var latencyTotal float32 = 0 + for _, value := range latencies { + latencyTotal += value + } + avg_latency_ms := latencyTotal / float32(len(latencies)) + packet_loss := (float32(count) - float32(replies)) / float32(count) - // TODO(mierdin): Implement ping test here - this is just a mock - time.Sleep(3000 * time.Millisecond) return map[string]string{ - "avg_latency_ms": "25.144", - "packet_loss_percentage": "0", + "avg_latency_ms": fmt.Sprintf("%.2f", avg_latency_ms), + "packet_loss": fmt.Sprintf("%.2f", packet_loss), }, nil } + +func pingTemp(count int) (float32, bool) { + return float32(count) * 4.234, true +} diff --git a/agent/testing/testlets/testlets.go b/agent/testing/testlets/testlets.go index 0ebb0c7..4458892 100644 --- a/agent/testing/testlets/testlets.go +++ b/agent/testing/testlets/testlets.go @@ -14,7 +14,8 @@ import ( var ( testletsMu sync.RWMutex testlets = make(map[string]Testlet) - done = make(chan error) + done = make(chan error) // Used from within the goroutine to inform the infrastructure it has finished + kill = make(chan bool) // Used from outside the goroutine to inform the goroutine to stop ) // Testlet defines what a testlet should look like if built in native @@ -43,14 +44,14 @@ type Testlet interface { // The developer of a native testlet just needs to implement the testlet logic here, // without worrying about things like managing goroutines or channels. That's all // managed by the "Run" or "Kill" functions - RunTestlet(string, []string) (map[string]string, error) + RunTestlet(string, []string, chan bool) (map[string]string, error) // TODO(mierdin): is this really the best name for it? Maybe something that's less confusing, less like "Run" // All testlets must be able to stop operation when sent a Kill command. Kill() error } -type rtfunc func(target string, args []string) (map[string]string, error) +type rtfunc func(target string, args []string, kill chan bool) (map[string]string, error) type BaseTestlet struct { @@ -66,11 +67,12 @@ func (b BaseTestlet) Run(target string, args []string, timeLimit int) (map[strin // TODO(mierdin): ensure channel is nil // done = make(chan error) + // kill = make(chan bool) // TODO(mierdin): Based on experimentation, this will keep running even if this function returns. // Need to be sure about how this ends. Also might want to evaluate the same for the existing non-native model, likely has the same issue go func() { - theseMetrics, err := b.RunFunction(target, args) + theseMetrics, err := b.RunFunction(target, args, kill) metrics = theseMetrics //TODO(mierdin): Gross. done <- err }() From e1eda3aa6553408904e4585a71d5015d70dffe55 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Mon, 1 Aug 2016 18:50:16 -0700 Subject: [PATCH 073/120] Catch-up commit Signed-off-by: Matt Oswalt --- .gitignore | 3 +- Makefile | 3 +- agent/testing/{testlets => bashtestlets}/http | 0 .../testing/{testlets => bashtestlets}/iperf | 0 .../_ping => bashtestlets/notatallping} | 0 agent/testing/testlets/ping/ping.go | 97 ------------------- assets/assets_unpack.go | 66 ++++++------- cmd/todd-agent/main.go | 2 +- cmd/todd-server/assets.go | 2 +- docs/nativetests.rst | 16 ++- scripts/buildtestlets.sh | 69 +++++++++++++ scripts/start-containers.sh | 2 +- 12 files changed, 124 insertions(+), 136 deletions(-) rename agent/testing/{testlets => bashtestlets}/http (100%) rename agent/testing/{testlets => bashtestlets}/iperf (100%) rename agent/testing/{testlets/_ping => bashtestlets/notatallping} (100%) delete mode 100644 agent/testing/testlets/ping/ping.go create mode 100755 scripts/buildtestlets.sh diff --git a/.gitignore b/.gitignore index 7c8cfbd..9896fa8 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ docs/_build/ preso_secret/ client/repeattest.sh *.out -*.DS_Store* \ No newline at end of file +*.DS_Store* +agent/testing/downloaded_testlets/ \ No newline at end of file diff --git a/Makefile b/Makefile index 7e21cf1..d5e11f3 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,7 @@ build: docker build -t mierdin/todd -f Dockerfile . compile: + ./scripts/buildtestlets.sh go install ./cmd/... install: configureenv @@ -27,7 +28,7 @@ update_deps: update_assets: go get -u github.com/jteeuwen/go-bindata/... - go-bindata -o assets/assets_unpack.go -pkg="assets" -prefix="agent" agent/testing/testlets/... agent/facts/collectors/... + go-bindata -o assets/assets_unpack.go -pkg="assets" -prefix="agent" agent/testing/bashtestlets/... agent/facts/collectors/... start: compile diff --git a/agent/testing/testlets/http b/agent/testing/bashtestlets/http similarity index 100% rename from agent/testing/testlets/http rename to agent/testing/bashtestlets/http diff --git a/agent/testing/testlets/iperf b/agent/testing/bashtestlets/iperf similarity index 100% rename from agent/testing/testlets/iperf rename to agent/testing/bashtestlets/iperf diff --git a/agent/testing/testlets/_ping b/agent/testing/bashtestlets/notatallping similarity index 100% rename from agent/testing/testlets/_ping rename to agent/testing/bashtestlets/notatallping diff --git a/agent/testing/testlets/ping/ping.go b/agent/testing/testlets/ping/ping.go deleted file mode 100644 index 42cc8d3..0000000 --- a/agent/testing/testlets/ping/ping.go +++ /dev/null @@ -1,97 +0,0 @@ -package toddping - -// NOTE ////////////////// -// -// This is a built-in testlet. Currently, the approach is to have each -// testlet under it's own package, which is explicitly imported under -// the ToDD agent's 'main' package. -// -// Currently, each testlet is stored within the ToDD repo in order to -// vet out the architecture, which means they are awkwardly placed -// underneath the "testlets" directory together. However, this should be -// a temporary holding place, as the main effort around native testlets -// is being implemented so they can be broken out into their own repos. - -import ( - "fmt" - "time" - - log "github.com/Sirupsen/logrus" - - "github.com/Mierdin/todd/agent/testing/testlets" -) - -type PingTestlet struct { - testlets.BaseTestlet -} - -func init() { - - var pt = PingTestlet{} - - // Ensure the RunFunction attribute is set correctly. - // This allows the underlying testlet infrastructure - // to know what function to call at runtime - pt.RunFunction = pt.RunTestlet - - // This is important - register the name of this testlet - // (the name the user will use in a testrun definition) - testlets.Register("ping", &pt) -} - -// RunTestlet implements the core logic of the testlet. Don't worry about running asynchronously, -// that's handled by the infrastructure. -func (p PingTestlet) RunTestlet(target string, args []string, kill chan (bool)) (map[string]string, error) { - - // Get number of pings - count := 3 //TODO(mierdin): need to parse from 'args', or if omitted, use a default value - - log.Error(args) - - var latencies []float32 - var replies int - - // Execute ping once per count - i := 0 - for i < count { - select { - case <-kill: - // Terminating early; return empty metrics - return map[string]string{}, nil - default: - - //log.Debugf("Executing ping #%d", i) - - // Mocked ping logic - latency, replyReceived := pingTemp(count) - - latencies = append(latencies, latency) - - if replyReceived { - replies += 1 - } - - i += 1 - time.Sleep(1000 * time.Millisecond) - - } - } - - // Calculate metrics - var latencyTotal float32 = 0 - for _, value := range latencies { - latencyTotal += value - } - avg_latency_ms := latencyTotal / float32(len(latencies)) - packet_loss := (float32(count) - float32(replies)) / float32(count) - - return map[string]string{ - "avg_latency_ms": fmt.Sprintf("%.2f", avg_latency_ms), - "packet_loss": fmt.Sprintf("%.2f", packet_loss), - }, nil - -} - -func pingTemp(count int) (float32, bool) { - return float32(count) * 4.234, true -} diff --git a/assets/assets_unpack.go b/assets/assets_unpack.go index 8f23ace..2a4a303 100644 --- a/assets/assets_unpack.go +++ b/assets/assets_unpack.go @@ -1,8 +1,8 @@ // Code generated by go-bindata. // sources: -// agent/testing/testlets/http -// agent/testing/testlets/iperf -// agent/testing/testlets/ping +// agent/testing/bashtestlets/http +// agent/testing/bashtestlets/iperf +// agent/testing/bashtestlets/notatallping // agent/facts/collectors/get_addresses // agent/facts/collectors/get_hostname // DO NOT EDIT! @@ -72,62 +72,62 @@ func (fi bindataFileInfo) Sys() interface{} { return nil } -var _testingTestletsHttp = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xa4\x97\x5f\x53\xdb\x38\x17\xc6\xef\xfd\x29\xce\x6b\xf2\x36\xa5\x03\x71\xc3\xc5\x5e\xd0\x81\x59\x1a\x42\xa1\x0d\x7f\x9a\xc0\xec\xee\x34\x9d\x8c\x63\x2b\x89\xa7\x8e\xe4\xb5\xe4\xb0\x2c\xf0\xdd\xf7\x48\xb2\x65\xc9\xa1\x21\xb3\xbb\x37\x6b\x1d\xff\xce\x63\xe9\xe8\x9c\x27\x74\xe7\x7f\x41\xc1\xf3\x60\x9a\xd0\x80\xd0\x15\x4c\x43\xbe\xf0\xbc\x1d\xe8\xb1\xec\x21\x4f\xe6\x0b\x01\x07\xef\xbb\xbf\xc0\x65\x28\x04\x5c\xf3\xfb\x30\x15\x1d\xb8\xe3\x04\x58\x0e\x4b\x16\x27\xb3\x24\x0a\x45\xc2\x28\xb0\x19\x88\x45\xc2\x31\x93\xb3\x22\x8f\x08\x44\x2c\x26\x90\x70\x98\xb3\x15\xc9\x29\x89\x61\xfa\x80\x04\x81\x34\x89\x08\x45\x81\x2c\x67\xab\x24\xc6\xf8\x82\xe4\xe4\x10\xf3\x16\x42\x64\xfc\x30\x08\xe6\x89\x58\x14\xd3\x4e\xc4\x96\xc1\x65\x42\xf2\x18\x37\x26\x58\x1c\x07\xd3\x94\x4d\x83\x65\xc8\x05\xc9\x83\xc1\x45\xaf\x7f\x35\xea\x7b\x5e\xb4\x20\xd1\x8f\xb7\xbb\xf0\xe8\x01\xfe\x47\xa2\x05\x03\x7f\x58\x50\x9a\xd0\x39\x24\x14\xd4\x6b\xb9\x51\xe2\x2b\x00\x45\x97\x21\x8d\x61\x7f\x05\x51\x91\xa7\x70\x1c\xc4\x64\x15\xd0\x22\x4d\xe1\xe0\xf8\x4d\x17\x9e\x9e\xe0\x51\xab\x1c\xbf\x39\x00\x5f\x31\x78\x06\xca\x04\xaa\x71\x11\xa6\x29\x89\x3b\xd0\xff\x2b\x11\xf8\x81\x8e\xff\x01\x08\x3e\x42\xf7\x03\x3c\x5b\xdf\xef\x99\x8f\xc2\xcd\xc9\x68\xd4\x3f\xd5\xdf\x56\xe8\x7b\xef\xd9\xf3\x92\x19\x7c\xfb\x06\xad\x2e\x1c\x1d\xe1\x37\x24\xed\xc3\xf7\xef\x1f\x90\xc2\x02\x51\xbd\x51\x19\xf5\x66\x89\xbc\x8a\x5b\xac\xda\x2c\xc9\xb9\x80\x30\x9f\x17\x4b\x42\xf1\x61\x86\x55\x50\xe5\x14\x84\x8b\x94\x08\xa0\xe1\x92\xc0\x7d\x82\x07\x39\x19\xfc\x76\xf2\xc7\x08\xa6\x44\xbf\xc7\x1c\x82\x77\x36\x62\x7b\xf2\x8b\x78\x98\x30\xbd\x0f\x1f\xb8\xf5\x72\x0f\x64\x49\xf0\x6c\x46\x9f\xc3\x2c\x67\x4b\x68\x1d\xa8\x37\x45\x06\x7c\xc1\x8a\x34\x96\x9a\x59\xc8\x39\x5e\x5a\x42\x05\x53\x12\x61\x96\x55\x55\xf5\xf0\x79\x82\x12\xfc\xc8\x6f\xfd\xea\xd7\xab\xd6\x63\xf5\xb8\xd3\x7a\x0f\xcf\xb0\x03\x43\xb2\xc4\xae\x80\x5b\x6b\xef\x2f\xe3\x5d\x07\x57\xbb\xc5\x82\x84\x42\xf5\x1a\x64\x0c\xb7\xb1\x07\x15\x6e\xed\x52\x1e\x46\x2c\x42\xd1\xe6\x90\x92\x99\xf0\x3c\x56\x88\xac\x10\x47\xad\xb7\xea\x4a\xf7\x39\xec\x33\xa8\xef\x7e\xff\x1e\xc6\xaa\xec\xed\xb1\x2e\xff\x98\x9e\xdf\xde\xde\xe0\x10\xc4\xe4\x10\xfe\xff\x28\x5b\x73\x22\x1b\xfa\x19\x26\xfd\xab\xd3\x13\x43\x0d\x18\xfb\x81\xe5\x11\xc9\x52\x71\xf2\xff\x13\x79\x9a\x54\xc5\x35\xfd\xd1\xd0\x3d\x46\x29\x89\x84\x8b\x47\x3a\xa8\xd9\x9e\x61\x6f\x72\xf2\xfb\x4c\xde\xb1\xcd\x66\x39\x11\x79\x48\x39\xbe\xd0\xfc\xa9\xe1\x47\x78\x97\x62\x3d\x83\xcb\xb0\x9b\xd3\x37\x39\xb7\x0c\x3b\xda\xe5\x85\x0c\x69\xee\xcc\xde\xb7\x90\x5d\x27\x1e\x32\x45\x46\x7a\x3d\x91\x6b\xcd\x7e\x6a\xd6\x4d\x9d\xc9\x2a\x9d\x75\xc6\x73\xc3\x5e\x15\xcb\x29\x6e\x18\xad\xa3\x4c\x40\x23\xe1\x32\x89\x16\xcb\x2a\x87\xeb\xa4\x8b\x17\x92\x86\x24\x4e\xf2\x46\x56\x5e\xc6\xca\xb4\xcf\x75\x7d\x92\xbf\x89\x4c\x8a\xd9\x3d\x4d\x59\x18\xcb\x04\x8e\xb1\x49\x15\xd0\x09\x5f\xd6\x12\x16\x24\x8c\x49\xce\x0d\xaf\xd7\x9a\x1e\xac\xd1\x39\xf9\xb3\xc0\xae\x36\x74\xb9\xd6\xf8\xe5\x1a\x5e\x64\xce\x5e\xf4\x52\xc3\x57\x06\x3e\x59\xcd\xe1\xb4\xdc\x25\x8c\x32\x42\x74\x82\x7c\x68\xec\xfe\xda\xc9\xb9\xcb\x5e\xca\xb0\xbf\x71\x63\xf8\xaa\x96\x6e\x37\x54\xd5\xd4\xf4\xd7\xba\x71\xf0\x25\xa0\x01\xa8\xee\x82\xaa\xbd\x36\x35\xdd\xb0\x1e\x19\x74\x70\xb8\x1b\x0e\xe0\x8c\x08\x74\x39\xb5\x33\x9c\xc9\x09\x99\xcd\xe4\x5d\xae\xca\x8e\x1a\x99\x84\x36\xce\x66\xab\xbb\xeb\x79\x66\x0a\x71\x8c\x95\xd3\xb6\xf4\x54\xc3\x13\x48\x4b\xda\xa7\xd0\xe6\x41\xe7\x9d\x35\xb9\xe3\xb7\x9d\x77\xe3\x5d\x3d\xb3\x9d\x77\xc1\xb8\x1b\x64\xed\x5d\xaf\x31\xa5\x9b\xd5\x9c\x09\xb7\xf4\x3e\x36\xf4\xca\x76\xdd\x2c\xe6\x1a\x80\xa5\xd6\x6b\xa8\x59\x83\xbe\x59\xd1\xb5\x09\x4b\xf1\xb4\xa1\xe8\xdc\xc9\x66\xcd\xa6\x95\x58\xaa\xfd\x86\xaa\xb2\x8b\xcd\x6a\xb6\xc9\x58\x4a\x67\xb5\x92\x6d\x27\xaf\xd6\xcf\x32\x22\x4b\xed\x53\xad\x66\x1b\xce\x56\x8d\x52\x5a\x95\xa5\x76\x5e\xab\xd9\x4e\xb4\x59\xed\x27\x66\x66\xc9\x5e\xb8\xb2\xc6\xaa\xb6\xd5\x75\xfd\xce\x12\xfe\x5c\x0b\x3b\x96\xf6\xca\x35\x37\x1d\xd1\xd1\xfc\xd2\xd0\xd4\xb6\xb7\x9d\xa2\xb1\x4c\x4b\x6f\xd0\xd0\x2b\x8d\x71\x3b\x41\xe3\xaa\x96\xe0\x65\x43\x50\xfb\xda\x76\x7a\x95\xed\x5a\x72\x57\x96\x9c\xe3\xac\x9b\x15\x5f\xf2\x66\x4b\xf5\xba\xa9\xba\xcd\x2e\xd7\xbd\xdb\x52\xbc\x69\x4c\x60\xd5\x45\x9b\x25\x1b\xf6\x6e\xe9\x7d\xfd\xf7\x3e\xf1\xb3\x5f\x01\x4b\x7d\x58\xab\x3b\x1e\xff\x8a\xe7\xae\xfd\x44\x58\x92\xa3\x5a\xd2\xc3\x3f\x7e\xf1\xef\xc7\x49\x1c\x8a\xf0\xa8\xad\xff\x09\xe0\x9b\x5f\x09\xff\xd0\x6f\xb7\xcc\xaa\xed\xef\xe9\xf7\x0d\xf7\x57\x54\x23\xe6\xb2\xe5\xf8\xd7\x60\x19\x70\x29\xcb\xb1\x6b\xd2\x0a\xba\xb4\x53\xe5\x9a\x77\xc2\x6e\x86\x72\xda\x9a\x54\x4b\x43\xd8\x0e\xaa\x18\x3b\x60\x28\xdb\x19\xed\xe2\xb8\xa7\xb1\x1d\x4f\x51\x76\xc0\xa1\x8c\x81\x19\xcc\x44\x0c\xe7\xf8\x91\xe2\x9c\x88\xcb\x69\xdf\xa8\x29\xbd\x76\x99\xd2\x0a\x6a\xa8\x0c\xb8\x94\x1e\xb3\x1a\xd2\xeb\x9a\x71\x26\x5c\x63\x4e\xa8\x41\xda\x72\x56\xc0\xbd\xa1\xea\xf0\xf5\x25\x55\x91\xff\x70\xf7\xce\xd4\x28\xd8\x89\xb4\x7d\xef\xb9\xed\xe9\x51\xb2\x86\xc1\xfb\x27\x00\x00\xff\xff\x75\x36\xff\x66\xd5\x0f\x00\x00") +var _testingBashtestletsHttp = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xa4\x97\x5f\x53\xdb\x38\x17\xc6\xef\xfd\x29\xce\x6b\xf2\x36\xa5\x03\x71\xc3\xc5\x5e\xd0\x81\x59\x1a\x42\xa1\x0d\x7f\x9a\xc0\xec\xee\x34\x9d\x8c\x63\x2b\x89\xa7\x8e\xe4\xb5\xe4\xb0\x2c\xf0\xdd\xf7\x48\xb2\x65\xc9\xa1\x21\xb3\xbb\x37\x6b\x1d\xff\xce\x63\xe9\xe8\x9c\x27\x74\xe7\x7f\x41\xc1\xf3\x60\x9a\xd0\x80\xd0\x15\x4c\x43\xbe\xf0\xbc\x1d\xe8\xb1\xec\x21\x4f\xe6\x0b\x01\x07\xef\xbb\xbf\xc0\x65\x28\x04\x5c\xf3\xfb\x30\x15\x1d\xb8\xe3\x04\x58\x0e\x4b\x16\x27\xb3\x24\x0a\x45\xc2\x28\xb0\x19\x88\x45\xc2\x31\x93\xb3\x22\x8f\x08\x44\x2c\x26\x90\x70\x98\xb3\x15\xc9\x29\x89\x61\xfa\x80\x04\x81\x34\x89\x08\x45\x81\x2c\x67\xab\x24\xc6\xf8\x82\xe4\xe4\x10\xf3\x16\x42\x64\xfc\x30\x08\xe6\x89\x58\x14\xd3\x4e\xc4\x96\xc1\x65\x42\xf2\x18\x37\x26\x58\x1c\x07\xd3\x94\x4d\x83\x65\xc8\x05\xc9\x83\xc1\x45\xaf\x7f\x35\xea\x7b\x5e\xb4\x20\xd1\x8f\xb7\xbb\xf0\xe8\x01\xfe\x47\xa2\x05\x03\x7f\x58\x50\x9a\xd0\x39\x24\x14\xd4\x6b\xb9\x51\xe2\x2b\x00\x45\x97\x21\x8d\x61\x7f\x05\x51\x91\xa7\x70\x1c\xc4\x64\x15\xd0\x22\x4d\xe1\xe0\xf8\x4d\x17\x9e\x9e\xe0\x51\xab\x1c\xbf\x39\x00\x5f\x31\x78\x06\xca\x04\xaa\x71\x11\xa6\x29\x89\x3b\xd0\xff\x2b\x11\xf8\x81\x8e\xff\x01\x08\x3e\x42\xf7\x03\x3c\x5b\xdf\xef\x99\x8f\xc2\xcd\xc9\x68\xd4\x3f\xd5\xdf\x56\xe8\x7b\xef\xd9\xf3\x92\x19\x7c\xfb\x06\xad\x2e\x1c\x1d\xe1\x37\x24\xed\xc3\xf7\xef\x1f\x90\xc2\x02\x51\xbd\x51\x19\xf5\x66\x89\xbc\x8a\x5b\xac\xda\x2c\xc9\xb9\x80\x30\x9f\x17\x4b\x42\xf1\x61\x86\x55\x50\xe5\x14\x84\x8b\x94\x08\xa0\xe1\x92\xc0\x7d\x82\x07\x39\x19\xfc\x76\xf2\xc7\x08\xa6\x44\xbf\xc7\x1c\x82\x77\x36\x62\x7b\xf2\x8b\x78\x98\x30\xbd\x0f\x1f\xb8\xf5\x72\x0f\x64\x49\xf0\x6c\x46\x9f\xc3\x2c\x67\x4b\x68\x1d\xa8\x37\x45\x06\x7c\xc1\x8a\x34\x96\x9a\x59\xc8\x39\x5e\x5a\x42\x05\x53\x12\x61\x96\x55\x55\xf5\xf0\x79\x82\x12\xfc\xc8\x6f\xfd\xea\xd7\xab\xd6\x63\xf5\xb8\xd3\x7a\x0f\xcf\xb0\x03\x43\xb2\xc4\xae\x80\x5b\x6b\xef\x2f\xe3\x5d\x07\x57\xbb\xc5\x82\x84\x42\xf5\x1a\x64\x0c\xb7\xb1\x07\x15\x6e\xed\x52\x1e\x46\x2c\x42\xd1\xe6\x90\x92\x99\xf0\x3c\x56\x88\xac\x10\x47\xad\xb7\xea\x4a\xf7\x39\xec\x33\xa8\xef\x7e\xff\x1e\xc6\xaa\xec\xed\xb1\x2e\xff\x98\x9e\xdf\xde\xde\xe0\x10\xc4\xe4\x10\xfe\xff\x28\x5b\x73\x22\x1b\xfa\x19\x26\xfd\xab\xd3\x13\x43\x0d\x18\xfb\x81\xe5\x11\xc9\x52\x71\xf2\xff\x13\x79\x9a\x54\xc5\x35\xfd\xd1\xd0\x3d\x46\x29\x89\x84\x8b\x47\x3a\xa8\xd9\x9e\x61\x6f\x72\xf2\xfb\x4c\xde\xb1\xcd\x66\x39\x11\x79\x48\x39\xbe\xd0\xfc\xa9\xe1\x47\x78\x97\x62\x3d\x83\xcb\xb0\x9b\xd3\x37\x39\xb7\x0c\x3b\xda\xe5\x85\x0c\x69\xee\xcc\xde\xb7\x90\x5d\x27\x1e\x32\x45\x46\x7a\x3d\x91\x6b\xcd\x7e\x6a\xd6\x4d\x9d\xc9\x2a\x9d\x75\xc6\x73\xc3\x5e\x15\xcb\x29\x6e\x18\xad\xa3\x4c\x40\x23\xe1\x32\x89\x16\xcb\x2a\x87\xeb\xa4\x8b\x17\x92\x86\x24\x4e\xf2\x46\x56\x5e\xc6\xca\xb4\xcf\x75\x7d\x92\xbf\x89\x4c\x8a\xd9\x3d\x4d\x59\x18\xcb\x04\x8e\xb1\x49\x15\xd0\x09\x5f\xd6\x12\x16\x24\x8c\x49\xce\x0d\xaf\xd7\x9a\x1e\xac\xd1\x39\xf9\xb3\xc0\xae\x36\x74\xb9\xd6\xf8\xe5\x1a\x5e\x64\xce\x5e\xf4\x52\xc3\x57\x06\x3e\x59\xcd\xe1\xb4\xdc\x25\x8c\x32\x42\x74\x82\x7c\x68\xec\xfe\xda\xc9\xb9\xcb\x5e\xca\xb0\xbf\x71\x63\xf8\xaa\x96\x6e\x37\x54\xd5\xd4\xf4\xd7\xba\x71\xf0\x25\xa0\x01\xa8\xee\x82\xaa\xbd\x36\x35\xdd\xb0\x1e\x19\x74\x70\xb8\x1b\x0e\xe0\x8c\x08\x74\x39\xb5\x33\x9c\xc9\x09\x99\xcd\xe4\x5d\xae\xca\x8e\x1a\x99\x84\x36\xce\x66\xab\xbb\xeb\x79\x66\x0a\x71\x8c\x95\xd3\xb6\xf4\x54\xc3\x13\x48\x4b\xda\xa7\xd0\xe6\x41\xe7\x9d\x35\xb9\xe3\xb7\x9d\x77\xe3\x5d\x3d\xb3\x9d\x77\xc1\xb8\x1b\x64\xed\x5d\xaf\x31\xa5\x9b\xd5\x9c\x09\xb7\xf4\x3e\x36\xf4\xca\x76\xdd\x2c\xe6\x1a\x80\xa5\xd6\x6b\xa8\x59\x83\xbe\x59\xd1\xb5\x09\x4b\xf1\xb4\xa1\xe8\xdc\xc9\x66\xcd\xa6\x95\x58\xaa\xfd\x86\xaa\xb2\x8b\xcd\x6a\xb6\xc9\x58\x4a\x67\xb5\x92\x6d\x27\xaf\xd6\xcf\x32\x22\x4b\xed\x53\xad\x66\x1b\xce\x56\x8d\x52\x5a\x95\xa5\x76\x5e\xab\xd9\x4e\xb4\x59\xed\x27\x66\x66\xc9\x5e\xb8\xb2\xc6\xaa\xb6\xd5\x75\xfd\xce\x12\xfe\x5c\x0b\x3b\x96\xf6\xca\x35\x37\x1d\xd1\xd1\xfc\xd2\xd0\xd4\xb6\xb7\x9d\xa2\xb1\x4c\x4b\x6f\xd0\xd0\x2b\x8d\x71\x3b\x41\xe3\xaa\x96\xe0\x65\x43\x50\xfb\xda\x76\x7a\x95\xed\x5a\x72\x57\x96\x9c\xe3\xac\x9b\x15\x5f\xf2\x66\x4b\xf5\xba\xa9\xba\xcd\x2e\xd7\xbd\xdb\x52\xbc\x69\x4c\x60\xd5\x45\x9b\x25\x1b\xf6\x6e\xe9\x7d\xfd\xf7\x3e\xf1\xb3\x5f\x01\x4b\x7d\x58\xab\x3b\x1e\xff\x8a\xe7\xae\xfd\x44\x58\x92\xa3\x5a\xd2\xc3\x3f\x7e\xf1\xef\xc7\x49\x1c\x8a\xf0\xa8\xad\xff\x09\xe0\x9b\x5f\x09\xff\xd0\x6f\xb7\xcc\xaa\xed\xef\xe9\xf7\x0d\xf7\x57\x54\x23\xe6\xb2\xe5\xf8\xd7\x60\x19\x70\x29\xcb\xb1\x6b\xd2\x0a\xba\xb4\x53\xe5\x9a\x77\xc2\x6e\x86\x72\xda\x9a\x54\x4b\x43\xd8\x0e\xaa\x18\x3b\x60\x28\xdb\x19\xed\xe2\xb8\xa7\xb1\x1d\x4f\x51\x76\xc0\xa1\x8c\x81\x19\xcc\x44\x0c\xe7\xf8\x91\xe2\x9c\x88\xcb\x69\xdf\xa8\x29\xbd\x76\x99\xd2\x0a\x6a\xa8\x0c\xb8\x94\x1e\xb3\x1a\xd2\xeb\x9a\x71\x26\x5c\x63\x4e\xa8\x41\xda\x72\x56\xc0\xbd\xa1\xea\xf0\xf5\x25\x55\x91\xff\x70\xf7\xce\xd4\x28\xd8\x89\xb4\x7d\xef\xb9\xed\xe9\x51\xb2\x86\xc1\xfb\x27\x00\x00\xff\xff\x75\x36\xff\x66\xd5\x0f\x00\x00") -func testingTestletsHttpBytes() ([]byte, error) { +func testingBashtestletsHttpBytes() ([]byte, error) { return bindataRead( - _testingTestletsHttp, - "testing/testlets/http", + _testingBashtestletsHttp, + "testing/bashtestlets/http", ) } -func testingTestletsHttp() (*asset, error) { - bytes, err := testingTestletsHttpBytes() +func testingBashtestletsHttp() (*asset, error) { + bytes, err := testingBashtestletsHttpBytes() if err != nil { return nil, err } - info := bindataFileInfo{name: "testing/testlets/http", size: 4053, mode: os.FileMode(493), modTime: time.Unix(1462644938, 0)} + info := bindataFileInfo{name: "testing/bashtestlets/http", size: 4053, mode: os.FileMode(493), modTime: time.Unix(1463727404, 0)} a := &asset{bytes: bytes, info: info} return a, nil } -var _testingTestletsIperf = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x94\x54\x4d\x6f\xe3\x36\x10\xbd\xeb\x57\x4c\x19\x63\x95\x04\x8e\x15\xe7\xd0\x43\x02\x07\x4d\xd3\x1c\x02\x6c\xb6\xc1\x3a\xc5\xa2\xd8\x2c\x0c\x4a\x1a\x59\x6c\x24\x52\x20\x47\x56\x5d\xc7\xff\xbd\x43\xd1\x5f\xbb\xf0\x65\xed\x0b\xcd\x99\x79\x7c\xef\xcd\x8c\x4f\x7e\x49\x5a\x67\x93\x54\xe9\x04\xf5\x02\x52\xe9\xca\x28\x3a\x81\x7b\xd3\x2c\xad\x9a\x97\x04\x57\x97\xe3\x5f\xe1\x49\x12\xc1\x9f\xae\x93\x15\x8d\xe0\x2f\x87\x60\x2c\xd4\x26\x57\x85\xca\x24\x29\xa3\xc1\x14\x40\xa5\x72\x5c\xe9\x4c\x6b\x33\x84\xcc\xe4\x08\xca\xc1\xdc\x2c\xd0\x6a\xcc\x21\x5d\x72\x06\x42\xa5\x32\xd4\x0c\xd0\x58\xb3\x50\x39\xdf\x97\x68\xf1\x9a\xeb\x4a\xa2\xc6\x5d\x27\xc9\x5c\x51\xd9\xa6\xa3\xcc\xd4\xc9\x93\x42\x9b\x33\x31\x32\x79\x9e\xa4\x95\x49\x93\x5a\x3a\x42\x9b\x7c\x7c\xbc\x7f\xf8\x34\x7d\x88\xa2\xac\xc4\xec\xed\xf4\x0c\x56\x11\xf0\x07\xb3\xd2\x80\xf8\xdc\x6a\xad\xf4\x1c\x94\x86\x3e\xec\x89\xa2\xe8\x13\x18\xb4\x96\x3a\x87\x8b\x05\xa8\x06\x6d\x01\xb7\x49\x8e\x8b\x44\xb7\x55\x05\x57\xb7\x1f\xc6\xf0\xfe\x0e\xab\x00\x73\xfb\xe1\x0a\x44\x48\x62\x15\xda\x10\xe3\x39\x92\x55\x85\xf9\x08\x1e\xfe\x55\xc4\x4f\x8c\xc4\x0d\x20\x1f\x61\x7c\x03\xeb\x03\x06\xf7\xbb\x67\xe1\xf9\x6e\x3a\x7d\xf8\x23\xbc\xde\xa7\x5e\x46\xeb\x28\x52\x05\x7c\xfd\x0a\x83\x31\x4c\x26\x20\x7a\x92\x02\xbe\x7d\xbb\xe1\x2c\xb6\x48\x07\xaa\xfe\x36\x2a\x94\x6f\xc6\x0b\xfb\x56\x28\xeb\x08\xa4\x9d\xb7\x35\x6a\x3e\x14\xec\x43\x6f\x28\xa1\xa3\x0a\x09\xb4\xac\x11\x3a\xc5\x4a\xee\x3e\x7e\xb9\xfb\x7b\x0a\x29\x86\x38\xd7\x20\x77\x6d\x6a\x86\xfe\x45\x16\x23\xab\x4e\x2e\xdd\x41\x70\x08\xde\x14\xd6\xb6\xc3\x77\x50\x58\x53\xc3\xe0\xaa\x8f\xb4\x0d\xb8\xd2\xb4\x55\xee\x31\x1b\xe9\x1c\xb7\x4d\x69\x32\x3d\x84\x6c\x9a\xad\xaf\x11\x9f\x67\x0c\xe1\x26\x62\xf0\x9b\xd8\xff\x1a\xac\xb6\xc7\x93\xc1\x25\xac\xe1\x04\x3e\x63\xcd\x73\x01\x2f\x07\xdc\x8f\xa7\x8f\xbf\x4b\xef\xd9\xb2\x21\x92\xfa\x69\x83\xc6\x30\x8d\x21\x6c\xd3\x0f\x58\x7a\x31\x54\x4a\x8a\x1d\x54\x58\x50\x70\x91\x2b\xb6\x6e\xb5\x0e\xdd\x46\x3d\xb8\x36\x75\xa4\xa8\xf5\x83\x3c\xe4\xf9\xe5\x0a\x5f\x57\xcb\x37\xe4\x98\xc5\x1e\x08\xb8\x67\xdc\x3e\xee\xa1\x63\x77\x78\xb6\x7a\xed\xfc\xe8\x10\x3a\x04\x8b\x4d\x25\x79\xea\x39\xa3\xe3\xf9\x0d\xc1\x8c\x5a\x59\x6d\x1f\x79\x7c\x1e\x1d\x28\x3c\xed\x07\x65\xb0\x23\xfe\x0e\xde\xd3\xd8\x25\xab\xd5\xb6\x60\xbd\x4e\x62\x31\x18\x8b\x38\x89\xcf\x3c\xfd\x4f\x86\x36\x4c\x3a\x8c\x99\x54\x61\x6c\xe6\xc7\xbc\xc6\xb9\x4c\x15\xeb\xea\x37\x89\x85\xf9\x4b\x71\x51\x40\x2d\xbc\x16\x26\x97\x49\x0d\xa5\x5c\xf8\x9d\xc3\x5c\x65\x24\xd3\xca\xb7\xd1\xfa\xcc\x11\x7c\xe1\x73\xeb\xfd\xc4\x3a\xf8\x8a\x80\xdc\x73\x67\xc2\x63\x52\x2f\xb9\xbd\xba\xe0\xbd\xf5\x23\xef\x8d\xb3\xfb\xe5\xed\xc9\x77\x46\xc7\xb4\x4b\xda\x1a\xa0\x5c\x10\x6c\x5b\x3d\xdb\xcc\xc7\x64\xb3\x50\x7b\xdd\x9e\x26\x88\x28\x32\x2d\x31\x09\xf6\x65\xf0\x43\x09\x4b\xa7\xac\x99\x75\x4a\xe7\xa6\x9b\x39\xf5\x1f\xce\xea\x74\x49\xb8\xb3\x30\x54\xb2\x81\x64\x21\x7e\xd5\x31\xf8\x6f\xb0\xf3\x42\x7b\x47\x47\xe7\x2f\xf7\xcf\x10\x00\xc0\x03\x5c\xc3\xeb\xe9\xe8\xfc\xf5\x6c\x74\x9e\xbc\x8e\x93\x26\xde\x9b\x0f\x4f\xbf\x33\x34\xdf\xb3\xe5\x00\x7e\x62\xd8\x80\x8a\xff\x72\xfa\x04\xde\x7e\xc2\x7e\xf9\xc2\x98\x79\xd1\x3c\x69\x1a\x33\x74\x4e\xda\x25\xa4\xcc\xe3\x91\x7d\xd8\xc4\x1c\xb2\xa3\xbc\x24\xbe\x99\xb4\xdb\x60\xa3\xd1\x5f\x76\xc6\xbe\xc1\x3f\x2d\x5f\x78\x47\x30\xa3\x6a\x19\x91\x95\xda\x15\x68\x83\x42\xf7\x33\x12\x1d\x66\x70\x1e\x64\x05\x11\x6e\xab\xee\x2c\x4a\xd9\xc6\x4e\xe5\x54\x32\xae\x22\x37\xe3\xdc\x9f\x81\x0e\x70\x7b\x74\x8f\xb1\x07\x8f\x38\xc6\xeb\x32\xcb\x25\xc9\x49\xbc\x12\x47\x9b\x25\xae\x45\x3c\x38\x1a\x89\xc5\x10\xc4\x0f\xba\x43\xf6\xf7\x77\x7d\xde\x11\x21\x7d\xee\x91\xfb\x58\xac\xe3\x28\x48\x3c\x20\x18\xfd\x1f\x00\x00\xff\xff\x4d\x99\x39\x1a\xed\x06\x00\x00") +var _testingBashtestletsIperf = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x94\x54\x4d\x6f\xe3\x36\x10\xbd\xeb\x57\x4c\x19\x63\x95\x04\x8e\x15\xe7\xd0\x43\x02\x07\x4d\xd3\x1c\x02\x6c\xb6\xc1\x3a\xc5\xa2\xd8\x2c\x0c\x4a\x1a\x59\x6c\x24\x52\x20\x47\x56\x5d\xc7\xff\xbd\x43\xd1\x5f\xbb\xf0\x65\xed\x0b\xcd\x99\x79\x7c\xef\xcd\x8c\x4f\x7e\x49\x5a\x67\x93\x54\xe9\x04\xf5\x02\x52\xe9\xca\x28\x3a\x81\x7b\xd3\x2c\xad\x9a\x97\x04\x57\x97\xe3\x5f\xe1\x49\x12\xc1\x9f\xae\x93\x15\x8d\xe0\x2f\x87\x60\x2c\xd4\x26\x57\x85\xca\x24\x29\xa3\xc1\x14\x40\xa5\x72\x5c\xe9\x4c\x6b\x33\x84\xcc\xe4\x08\xca\xc1\xdc\x2c\xd0\x6a\xcc\x21\x5d\x72\x06\x42\xa5\x32\xd4\x0c\xd0\x58\xb3\x50\x39\xdf\x97\x68\xf1\x9a\xeb\x4a\xa2\xc6\x5d\x27\xc9\x5c\x51\xd9\xa6\xa3\xcc\xd4\xc9\x93\x42\x9b\x33\x31\x32\x79\x9e\xa4\x95\x49\x93\x5a\x3a\x42\x9b\x7c\x7c\xbc\x7f\xf8\x34\x7d\x88\xa2\xac\xc4\xec\xed\xf4\x0c\x56\x11\xf0\x07\xb3\xd2\x80\xf8\xdc\x6a\xad\xf4\x1c\x94\x86\x3e\xec\x89\xa2\xe8\x13\x18\xb4\x96\x3a\x87\x8b\x05\xa8\x06\x6d\x01\xb7\x49\x8e\x8b\x44\xb7\x55\x05\x57\xb7\x1f\xc6\xf0\xfe\x0e\xab\x00\x73\xfb\xe1\x0a\x44\x48\x62\x15\xda\x10\xe3\x39\x92\x55\x85\xf9\x08\x1e\xfe\x55\xc4\x4f\x8c\xc4\x0d\x20\x1f\x61\x7c\x03\xeb\x03\x06\xf7\xbb\x67\xe1\xf9\x6e\x3a\x7d\xf8\x23\xbc\xde\xa7\x5e\x46\xeb\x28\x52\x05\x7c\xfd\x0a\x83\x31\x4c\x26\x20\x7a\x92\x02\xbe\x7d\xbb\xe1\x2c\xb6\x48\x07\xaa\xfe\x36\x2a\x94\x6f\xc6\x0b\xfb\x56\x28\xeb\x08\xa4\x9d\xb7\x35\x6a\x3e\x14\xec\x43\x6f\x28\xa1\xa3\x0a\x09\xb4\xac\x11\x3a\xc5\x4a\xee\x3e\x7e\xb9\xfb\x7b\x0a\x29\x86\x38\xd7\x20\x77\x6d\x6a\x86\xfe\x45\x16\x23\xab\x4e\x2e\xdd\x41\x70\x08\xde\x14\xd6\xb6\xc3\x77\x50\x58\x53\xc3\xe0\xaa\x8f\xb4\x0d\xb8\xd2\xb4\x55\xee\x31\x1b\xe9\x1c\xb7\x4d\x69\x32\x3d\x84\x6c\x9a\xad\xaf\x11\x9f\x67\x0c\xe1\x26\x62\xf0\x9b\xd8\xff\x1a\xac\xb6\xc7\x93\xc1\x25\xac\xe1\x04\x3e\x63\xcd\x73\x01\x2f\x07\xdc\x8f\xa7\x8f\xbf\x4b\xef\xd9\xb2\x21\x92\xfa\x69\x83\xc6\x30\x8d\x21\x6c\xd3\x0f\x58\x7a\x31\x54\x4a\x8a\x1d\x54\x58\x50\x70\x91\x2b\xb6\x6e\xb5\x0e\xdd\x46\x3d\xb8\x36\x75\xa4\xa8\xf5\x83\x3c\xe4\xf9\xe5\x0a\x5f\x57\xcb\x37\xe4\x98\xc5\x1e\x08\xb8\x67\xdc\x3e\xee\xa1\x63\x77\x78\xb6\x7a\xed\xfc\xe8\x10\x3a\x04\x8b\x4d\x25\x79\xea\x39\xa3\xe3\xf9\x0d\xc1\x8c\x5a\x59\x6d\x1f\x79\x7c\x1e\x1d\x28\x3c\xed\x07\x65\xb0\x23\xfe\x0e\xde\xd3\xd8\x25\xab\xd5\xb6\x60\xbd\x4e\x62\x31\x18\x8b\x38\x89\xcf\x3c\xfd\x4f\x86\x36\x4c\x3a\x8c\x99\x54\x61\x6c\xe6\xc7\xbc\xc6\xb9\x4c\x15\xeb\xea\x37\x89\x85\xf9\x4b\x71\x51\x40\x2d\xbc\x16\x26\x97\x49\x0d\xa5\x5c\xf8\x9d\xc3\x5c\x65\x24\xd3\xca\xb7\xd1\xfa\xcc\x11\x7c\xe1\x73\xeb\xfd\xc4\x3a\xf8\x8a\x80\xdc\x73\x67\xc2\x63\x52\x2f\xb9\xbd\xba\xe0\xbd\xf5\x23\xef\x8d\xb3\xfb\xe5\xed\xc9\x77\x46\xc7\xb4\x4b\xda\x1a\xa0\x5c\x10\x6c\x5b\x3d\xdb\xcc\xc7\x64\xb3\x50\x7b\xdd\x9e\x26\x88\x28\x32\x2d\x31\x09\xf6\x65\xf0\x43\x09\x4b\xa7\xac\x99\x75\x4a\xe7\xa6\x9b\x39\xf5\x1f\xce\xea\x74\x49\xb8\xb3\x30\x54\xb2\x81\x64\x21\x7e\xd5\x31\xf8\x6f\xb0\xf3\x42\x7b\x47\x47\xe7\x2f\xf7\xcf\x10\x00\xc0\x03\x5c\xc3\xeb\xe9\xe8\xfc\xf5\x6c\x74\x9e\xbc\x8e\x93\x26\xde\x9b\x0f\x4f\xbf\x33\x34\xdf\xb3\xe5\x00\x7e\x62\xd8\x80\x8a\xff\x72\xfa\x04\xde\x7e\xc2\x7e\xf9\xc2\x98\x79\xd1\x3c\x69\x1a\x33\x74\x4e\xda\x25\xa4\xcc\xe3\x91\x7d\xd8\xc4\x1c\xb2\xa3\xbc\x24\xbe\x99\xb4\xdb\x60\xa3\xd1\x5f\x76\xc6\xbe\xc1\x3f\x2d\x5f\x78\x47\x30\xa3\x6a\x19\x91\x95\xda\x15\x68\x83\x42\xf7\x33\x12\x1d\x66\x70\x1e\x64\x05\x11\x6e\xab\xee\x2c\x4a\xd9\xc6\x4e\xe5\x54\x32\xae\x22\x37\xe3\xdc\x9f\x81\x0e\x70\x7b\x74\x8f\xb1\x07\x8f\x38\xc6\xeb\x32\xcb\x25\xc9\x49\xbc\x12\x47\x9b\x25\xae\x45\x3c\x38\x1a\x89\xc5\x10\xc4\x0f\xba\x43\xf6\xf7\x77\x7d\xde\x11\x21\x7d\xee\x91\xfb\x58\xac\xe3\x28\x48\x3c\x20\x18\xfd\x1f\x00\x00\xff\xff\x4d\x99\x39\x1a\xed\x06\x00\x00") -func testingTestletsIperfBytes() ([]byte, error) { +func testingBashtestletsIperfBytes() ([]byte, error) { return bindataRead( - _testingTestletsIperf, - "testing/testlets/iperf", + _testingBashtestletsIperf, + "testing/bashtestlets/iperf", ) } -func testingTestletsIperf() (*asset, error) { - bytes, err := testingTestletsIperfBytes() +func testingBashtestletsIperf() (*asset, error) { + bytes, err := testingBashtestletsIperfBytes() if err != nil { return nil, err } - info := bindataFileInfo{name: "testing/testlets/iperf", size: 1773, mode: os.FileMode(493), modTime: time.Unix(1462644938, 0)} + info := bindataFileInfo{name: "testing/bashtestlets/iperf", size: 1773, mode: os.FileMode(493), modTime: time.Unix(1463727404, 0)} a := &asset{bytes: bytes, info: info} return a, nil } -var _testingTestletsPing = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x94\x93\x4f\x4f\xdb\x4a\x14\xc5\xf7\xf3\x29\xce\x1b\xfc\x70\x82\x20\x43\xb2\x78\x0b\x50\xd0\x43\xbc\x2c\x9e\x04\x2d\x6a\x5a\x55\x15\x41\xd1\xc4\xbe\x89\x47\xd8\x33\x96\x67\x1c\x1a\x25\x7c\xf7\x5e\xdb\x05\xac\xaa\x9b\xc2\xe6\x66\xce\xb9\xbf\xfb\x2f\x39\xfa\x4b\xd5\xbe\x52\x2b\x63\x15\xd9\x2d\x56\xda\x67\x42\x1c\xe1\xc6\x95\xbb\xca\x6c\xb2\x80\xc9\xf9\xf8\x1f\xdc\xe9\x10\xf0\xd1\x3f\xeb\x3c\x8c\xf0\xc5\x13\x5c\x85\xc2\xa5\x66\x6d\x12\x1d\x8c\xb3\x70\x6b\x84\xcc\x78\xce\xf4\xae\xae\x12\x42\xe2\x52\x82\xf1\xd8\xb8\x2d\x55\x96\x52\xac\x76\xec\x20\xe4\x26\x21\xcb\x80\xb2\x72\x5b\x93\xf2\x7b\x46\x15\x5d\x70\x5e\x16\x42\xe9\x2f\x94\xda\x98\x90\xd5\xab\x51\xe2\x0a\x75\x67\xa8\x4a\xb9\xb1\xe0\xd2\x54\xad\x72\xb7\x52\x85\xf6\x81\x2a\x75\xfb\xff\xcd\xec\xc3\x7c\x26\x44\x92\x51\xf2\x34\x18\x62\x2f\xc0\x7f\x94\x64\x0e\xf2\x53\x6d\xad\xb1\x1b\x18\x8b\x56\x6e\x1a\x25\xd9\x1a\x18\x5a\x68\x9b\xe2\x6c\x8b\xb2\xb1\x5c\xa9\x94\xb6\xca\xd6\x79\x8e\xc9\xd5\xf1\x18\x87\x03\xf6\x1d\xe5\xea\x78\x02\x79\xdf\x62\x3c\xac\x0b\x4c\xf3\x41\xe7\x39\xa5\x23\xcc\xbe\x9b\xc0\xca\x48\x5e\x82\x38\xc4\xf8\x12\x2f\xbd\xfa\x37\x6f\x45\x71\x7f\x3d\x9f\xcf\xfe\xeb\x6a\xb7\xd6\x73\xf1\x22\x84\x59\xe3\xe1\x01\xd1\x18\xd3\x29\x64\xdb\xa2\xc4\xe3\xe3\x25\xbb\x78\x41\xb6\x6b\xb4\x79\x15\x6b\xd3\x9c\xe2\x33\x6f\x6d\x6d\x2a\x1f\xa0\xab\x4d\x5d\x90\xe5\x60\xcd\x5b\x68\xd7\x19\xc8\x87\x9c\x02\xac\x2e\x08\xcf\x86\x07\xb9\xbe\xfd\x7a\xfd\x6d\x8e\x15\x75\x3a\xe7\x10\xdf\x6c\xee\x4e\x9b\x8a\x3c\x8c\xce\x9f\xf5\xce\xf7\xc4\x53\x34\x2b\xe1\xd9\xde\xf8\x1e\xeb\xca\x15\x88\x26\xad\x52\x97\xf0\x99\xab\xf3\xb4\x61\x96\xda\x7b\x3e\x9a\xb1\xc1\xb5\x08\x5d\x96\xaf\x5b\x15\x1c\x2f\x19\xe1\xa7\x32\xfa\x57\xbe\x7f\x8a\xf6\xaf\xe1\x51\x74\xce\x9b\xfa\xad\x30\x66\xa1\x55\xaa\xda\x2e\x7f\x02\xa7\xb2\x3d\x52\xf4\xea\xe2\x01\xa4\x10\xae\x0e\x65\x1d\xa6\xd1\x20\xfa\xc5\x3e\x14\xa2\xd4\xc9\x13\x85\x65\xee\x3c\xd3\x07\xed\x39\xa2\xce\x8f\x03\x42\x85\x78\x61\x63\x34\xff\x07\x34\x53\x9c\x59\xc4\x5e\x8d\x4e\xb0\x18\x8c\x4e\x16\xc3\xbf\xd1\xe5\xa3\xc9\x1f\x9d\xa8\xc5\x58\x95\xf1\x50\xe8\xed\x66\x99\xeb\x40\x36\xd9\xfd\x09\x94\xbf\x59\x98\x82\xb9\xaa\xa3\x2f\x54\x13\xbf\x63\x05\x9f\x8e\x2f\xb7\x4c\x75\xd0\xd3\x78\x2f\x7b\xbd\x2f\x4b\xe2\x9f\x90\x0d\x7a\x43\xf2\x42\xc6\x51\x4f\x8a\xe5\x29\x64\xaf\xa3\x65\xe1\x5b\x4b\xef\x29\x96\x2f\xb1\xe8\xda\xec\x95\xf8\x11\x00\x00\xff\xff\x84\x27\x4a\xf0\xe8\x03\x00\x00") +var _testingBashtestletsNotatallping = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x94\x93\x4f\x4f\xdb\x4a\x14\xc5\xf7\xf3\x29\xce\x1b\xfc\x70\x82\x20\x43\xb2\x78\x0b\x50\xd0\x43\xbc\x2c\x9e\x04\x2d\x6a\x5a\x55\x15\x41\xd1\xc4\xbe\x89\x47\xd8\x33\x96\x67\x1c\x1a\x25\x7c\xf7\x5e\xdb\x05\xac\xaa\x9b\xc2\xe6\x66\xce\xb9\xbf\xfb\x2f\x39\xfa\x4b\xd5\xbe\x52\x2b\x63\x15\xd9\x2d\x56\xda\x67\x42\x1c\xe1\xc6\x95\xbb\xca\x6c\xb2\x80\xc9\xf9\xf8\x1f\xdc\xe9\x10\xf0\xd1\x3f\xeb\x3c\x8c\xf0\xc5\x13\x5c\x85\xc2\xa5\x66\x6d\x12\x1d\x8c\xb3\x70\x6b\x84\xcc\x78\xce\xf4\xae\xae\x12\x42\xe2\x52\x82\xf1\xd8\xb8\x2d\x55\x96\x52\xac\x76\xec\x20\xe4\x26\x21\xcb\x80\xb2\x72\x5b\x93\xf2\x7b\x46\x15\x5d\x70\x5e\x16\x42\xe9\x2f\x94\xda\x98\x90\xd5\xab\x51\xe2\x0a\x75\x67\xa8\x4a\xb9\xb1\xe0\xd2\x54\xad\x72\xb7\x52\x85\xf6\x81\x2a\x75\xfb\xff\xcd\xec\xc3\x7c\x26\x44\x92\x51\xf2\x34\x18\x62\x2f\xc0\x7f\x94\x64\x0e\xf2\x53\x6d\xad\xb1\x1b\x18\x8b\x56\x6e\x1a\x25\xd9\x1a\x18\x5a\x68\x9b\xe2\x6c\x8b\xb2\xb1\x5c\xa9\x94\xb6\xca\xd6\x79\x8e\xc9\xd5\xf1\x18\x87\x03\xf6\x1d\xe5\xea\x78\x02\x79\xdf\x62\x3c\xac\x0b\x4c\xf3\x41\xe7\x39\xa5\x23\xcc\xbe\x9b\xc0\xca\x48\x5e\x82\x38\xc4\xf8\x12\x2f\xbd\xfa\x37\x6f\x45\x71\x7f\x3d\x9f\xcf\xfe\xeb\x6a\xb7\xd6\x73\xf1\x22\x84\x59\xe3\xe1\x01\xd1\x18\xd3\x29\x64\xdb\xa2\xc4\xe3\xe3\x25\xbb\x78\x41\xb6\x6b\xb4\x79\x15\x6b\xd3\x9c\xe2\x33\x6f\x6d\x6d\x2a\x1f\xa0\xab\x4d\x5d\x90\xe5\x60\xcd\x5b\x68\xd7\x19\xc8\x87\x9c\x02\xac\x2e\x08\xcf\x86\x07\xb9\xbe\xfd\x7a\xfd\x6d\x8e\x15\x75\x3a\xe7\x10\xdf\x6c\xee\x4e\x9b\x8a\x3c\x8c\xce\x9f\xf5\xce\xf7\xc4\x53\x34\x2b\xe1\xd9\xde\xf8\x1e\xeb\xca\x15\x88\x26\xad\x52\x97\xf0\x99\xab\xf3\xb4\x61\x96\xda\x7b\x3e\x9a\xb1\xc1\xb5\x08\x5d\x96\xaf\x5b\x15\x1c\x2f\x19\xe1\xa7\x32\xfa\x57\xbe\x7f\x8a\xf6\xaf\xe1\x51\x74\xce\x9b\xfa\xad\x30\x66\xa1\x55\xaa\xda\x2e\x7f\x02\xa7\xb2\x3d\x52\xf4\xea\xe2\x01\xa4\x10\xae\x0e\x65\x1d\xa6\xd1\x20\xfa\xc5\x3e\x14\xa2\xd4\xc9\x13\x85\x65\xee\x3c\xd3\x07\xed\x39\xa2\xce\x8f\x03\x42\x85\x78\x61\x63\x34\xff\x07\x34\x53\x9c\x59\xc4\x5e\x8d\x4e\xb0\x18\x8c\x4e\x16\xc3\xbf\xd1\xe5\xa3\xc9\x1f\x9d\xa8\xc5\x58\x95\xf1\x50\xe8\xed\x66\x99\xeb\x40\x36\xd9\xfd\x09\x94\xbf\x59\x98\x82\xb9\xaa\xa3\x2f\x54\x13\xbf\x63\x05\x9f\x8e\x2f\xb7\x4c\x75\xd0\xd3\x78\x2f\x7b\xbd\x2f\x4b\xe2\x9f\x90\x0d\x7a\x43\xf2\x42\xc6\x51\x4f\x8a\xe5\x29\x64\xaf\xa3\x65\xe1\x5b\x4b\xef\x29\x96\x2f\xb1\xe8\xda\xec\x95\xf8\x11\x00\x00\xff\xff\x84\x27\x4a\xf0\xe8\x03\x00\x00") -func testingTestletsPingBytes() ([]byte, error) { +func testingBashtestletsNotatallpingBytes() ([]byte, error) { return bindataRead( - _testingTestletsPing, - "testing/testlets/ping", + _testingBashtestletsNotatallping, + "testing/bashtestlets/notatallping", ) } -func testingTestletsPing() (*asset, error) { - bytes, err := testingTestletsPingBytes() +func testingBashtestletsNotatallping() (*asset, error) { + bytes, err := testingBashtestletsNotatallpingBytes() if err != nil { return nil, err } - info := bindataFileInfo{name: "testing/testlets/ping", size: 1000, mode: os.FileMode(493), modTime: time.Unix(1462644962, 0)} + info := bindataFileInfo{name: "testing/bashtestlets/notatallping", size: 1000, mode: os.FileMode(493), modTime: time.Unix(1463727404, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -147,7 +147,7 @@ func factsCollectorsGet_addresses() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "facts/collectors/get_addresses", size: 421, mode: os.FileMode(493), modTime: time.Unix(1462644938, 0)} + info := bindataFileInfo{name: "facts/collectors/get_addresses", size: 421, mode: os.FileMode(493), modTime: time.Unix(1463727404, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -167,7 +167,7 @@ func factsCollectorsGet_hostname() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "facts/collectors/get_hostname", size: 227, mode: os.FileMode(493), modTime: time.Unix(1462644938, 0)} + info := bindataFileInfo{name: "facts/collectors/get_hostname", size: 227, mode: os.FileMode(493), modTime: time.Unix(1463727404, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -224,9 +224,9 @@ func AssetNames() []string { // _bindata is a table, holding each asset generator, mapped to its name. var _bindata = map[string]func() (*asset, error){ - "testing/testlets/http": testingTestletsHttp, - "testing/testlets/iperf": testingTestletsIperf, - "testing/testlets/ping": testingTestletsPing, + "testing/bashtestlets/http": testingBashtestletsHttp, + "testing/bashtestlets/iperf": testingBashtestletsIperf, + "testing/bashtestlets/notatallping": testingBashtestletsNotatallping, "facts/collectors/get_addresses": factsCollectorsGet_addresses, "facts/collectors/get_hostname": factsCollectorsGet_hostname, } @@ -278,10 +278,10 @@ var _bintree = &bintree{nil, map[string]*bintree{ }}, }}, "testing": &bintree{nil, map[string]*bintree{ - "testlets": &bintree{nil, map[string]*bintree{ - "http": &bintree{testingTestletsHttp, map[string]*bintree{}}, - "iperf": &bintree{testingTestletsIperf, map[string]*bintree{}}, - "ping": &bintree{testingTestletsPing, map[string]*bintree{}}, + "bashtestlets": &bintree{nil, map[string]*bintree{ + "http": &bintree{testingBashtestletsHttp, map[string]*bintree{}}, + "iperf": &bintree{testingBashtestletsIperf, map[string]*bintree{}}, + "notatallping": &bintree{testingBashtestletsNotatallping, map[string]*bintree{}}, }}, }}, }} diff --git a/cmd/todd-agent/main.go b/cmd/todd-agent/main.go index 30950d9..c70e208 100644 --- a/cmd/todd-agent/main.go +++ b/cmd/todd-agent/main.go @@ -25,7 +25,7 @@ import ( "github.com/Mierdin/todd/hostresources" // Testlet imports (importing these packages registers the testlets) - _ "github.com/Mierdin/todd/agent/testing/testlets/ping" + _ "github.com/Mierdin/todd/agent/testing/downloaded_testlets" ) // Command-line Arguments diff --git a/cmd/todd-server/assets.go b/cmd/todd-server/assets.go index fc649c1..6ff9695 100644 --- a/cmd/todd-server/assets.go +++ b/cmd/todd-server/assets.go @@ -32,7 +32,7 @@ func serveAssets(cfg config.Config) map[string]map[string]string { // Initialize asset map assetMap := map[string]map[string]string{ "factcollectors": hashAssets("facts/collectors"), - "testlets": hashAssets("testing/testlets"), + "testlets": hashAssets("testing/bashtestlets"), } fmt.Println(assetMap) diff --git a/docs/nativetests.rst b/docs/nativetests.rst index e6c04a3..10ec9d2 100644 --- a/docs/nativetests.rst +++ b/docs/nativetests.rst @@ -3,4 +3,18 @@ Native Tests Need to talk about the native tests you've built in, and turn the "testlets" doc into more of a "so you want to build your own, eh?" -Also need to figure out if you want to refer to both native and non-native as "testlets", or maybe reserve that for non-native \ No newline at end of file +Also need to figure out if you want to refer to both native and non-native as "testlets", or maybe reserve that for non-native + + + +Need a design guide outlining some requirements for native testlets: + +* Testlets must honor the "kill" channel passed to the RunTestlet function. If a "true" value is passed into that channel, the testlet must quit immediately. + +* Need to put some specifics together regarding testlets that provide some kind of "server" functionality, kind of like what you've done for custom testlets + +* How do args work in native testlets? It's a bit awkward to continue to use command-line style args in a native testlet but might be necessary to preserve consistency for the user. + +* How to handle vendoring? If a testlet uses a library to run the tests, should the library get vendored with the testlet's repository, or within ToDD proper? Probably the former, but how is it used in that case? Just need to have a strategy here. (You probably vendored such libs in todd proper in order to develop them, so make sure you remove them once they're not needed) + +* How are errors returned from the testlet logic? If a testlet returns an error, how is this handled? \ No newline at end of file diff --git a/scripts/buildtestlets.sh b/scripts/buildtestlets.sh new file mode 100755 index 0000000..2c47c0f --- /dev/null +++ b/scripts/buildtestlets.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +# Copyright 2016 Matt Oswalt. Use or modification of this +# source code is governed by the license provided here: +# https://github.com/mierdin/todd/blob/master/LICENSE + +# This script downloads ToDD testlets prior to compile + +set -e +set -u +set -o pipefail + + +testlets=( + 'https://github.com/Mierdin/todd-nativetestlet-ping.git' + ) + + +rm -rf testlettemp && mkdir testlettemp && cd testlettemp + +for i in "${testlets[@]}" +do + git clone $i +done + +cd .. + +rm -rf agent/testing/downloaded_testlets/ && mkdir agent/testing/downloaded_testlets + +for dir in ./testlettemp/*/ +do + dir=${dir%*/} + cp testlettemp/${dir##*/}/testlet/* agent/testing/downloaded_testlets + #echo ${dir##*/} +done + +rm -rf testlettemp + + + +# rebuild plugins: +# _debug "removing: ${plugin_dir:?}/*" +# rm -rf "${plugin_dir:?}/"* +# mkdir -p "${plugin_dir}" + +# _info "building plugins" +# find "${__proj_dir}/plugin/" -type d -iname "snap-*" -print0 | xargs -0 -n 1 -I{} "${__dir}/build_plugin.sh" {} + +#--------- + + + +# __dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# __proj_dir="$(dirname "$__dir")" + +# # shellcheck source=scripts/common.sh +# . "${__dir}/common.sh" + +# build_dir="${__proj_dir}/build" +# plugin_dir="${build_dir}/plugin" + +# plugin_src_path=$1 +# plugin_name=$(basename "${plugin_src_path}") +# go_build=(go build -a -ldflags "-w") + +# _debug "plugin source: ${plugin_src_path}" +# _info "building ${plugin_name}" + +# (cd "${plugin_src_path}" && "${go_build[@]}" -o "${plugin_dir}/${plugin_name}" . || exit 1) \ No newline at end of file diff --git a/scripts/start-containers.sh b/scripts/start-containers.sh index 92891b6..bf76115 100755 --- a/scripts/start-containers.sh +++ b/scripts/start-containers.sh @@ -2,7 +2,7 @@ # Copyright 2016 Matt Oswalt. Use or modification of this # source code is governed by the license provided here: -# https://github.com/mierdin/todd:$branch/blob/master/LICENSE +# https://github.com/mierdin/todd/blob/master/LICENSE # This script is designed to manage containers for ToDD. This could be start the basic infrastructure for ToDD like the etcd and rabbitmq containers, # or you could run with the "integration" arg, and run integration tests as well. From 9b50b20ffe20df61be37316dfd2dfecd23f60962 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Sun, 14 Aug 2016 22:56:40 -0700 Subject: [PATCH 074/120] Updating docs Signed-off-by: Matt Oswalt --- docs/customtestlets.rst | 71 +++++++++++++++++++++++++++++++++++ docs/dsl/group-datacenter.yml | 2 +- docs/index.rst | 1 + docs/testlets.rst | 67 +++++++-------------------------- etc/agent.cfg | 2 +- 5 files changed, 87 insertions(+), 56 deletions(-) create mode 100644 docs/customtestlets.rst diff --git a/docs/customtestlets.rst b/docs/customtestlets.rst new file mode 100644 index 0000000..a7023a0 --- /dev/null +++ b/docs/customtestlets.rst @@ -0,0 +1,71 @@ +Custom Testlets +================================ + +ToDD was originally built with no testlets built-in to the agent. All tests were performed using external executable files (i.e. scripts, binaries) that accept a standard set of input, run a test application, and provide a standard set of output containing metrics from that test. Though ToDD has since adopted a number of testlets to be built-in to the agent for simplicity, this functionality still remains, so you can extend ToDD to run whatever types of tests you wish. + +This allows the user to use any testing application (provided it is available on the system on which the ToDD agent is running, and specify which agents run this application. All of the complicated stuff with respect to sending arguments to the underlying testing application as well as parsing the output, is performed inside the testlet. + +.. image:: images/testlet.png + +The testlet is actually run by the ToDD agent, so if there are 3 agents participating in a test, then 3 testlets are running. All logic that performs the test should be contained within the + +Testrun Definition +------------------ + +When you want to run a certain testlet, you refer to it by name. There are a number of testlets built-in to ToDD and are therefore reserved: + +* http +* bandwidth +* ping + +You can, of course, build your own testlet (provided it follows the standard defined on this page) and refer to it by it's filename. + +Check Mode +---------- +Each testlet must support a "check mode". This is a way of running a testlet that allows the ToDD agent to know whether or not a test can be performed, without actually running the test. + +For instance, when the ToDD agent runs the "ping" testlet in check mode, it would invoke it like this: + +.. code-block:: text + + ./testletname check + +That said, the ToDD Server will distribute testrun instructions to the agents in two phases: + +* Install - run the referenced testlet in check mode, and record all params in local agent cache +* Execute - run the installed testrun instruction + +Input +----- +There is little to no similarity between various testing applications with respect to the parameters required by those applications. However, in order to simplify things for the ToDD agent, the testlet - due to it's place as a "wrapper" for a testing application - standardizes this input so the ToDD agent can invoke any testlet in a consistent manner + +.. code-block:: text + + ./testletname < target > < args > + +The ToDD agent will execute the testlet as it exists on the system, and will pass a few arguments to it (meaning the testlet must support and honor these arguments): + +* "target" - this is always the first parameter - represents the IP address or FQDN of the target for this test instance. +* "args" - any arguments required by the underlying application. These should be passed to that application via the testlet + +Output +------ +The output for every testlet is a single-level JSON object, which contains key-value pairs for the metrics gathered for that testlet. + +Since the ToDD agent is responsible for executing a testlet, it is also watching stdout for the testlet to provide this JSON object. This is one of the things that make testlets a very flexible method of performing tests - since it only needs to output these metrics as JSON to stdout, the testlet can be written in any language, as long as they support the arguments described in the "Input" section. + +A sample JSON object that the "ping" testlet will provide is shown below: + +.. code-block:: text + + { + "avg_latency_ms": "27.007", + "packet_loss_percentage": "0" + } + +This specific output covers the metrics for a single testlet run, which means that this is relevant to only a single target, run by a single ToDD agent. The ToDD agent will receive this output once for each target in the testrun, and submit this up to the ToDD server for collection. + +.. NOTE:: + The ToDD Server will also aggregate each agent's report to a single metric document for the entire testrun, so that it's easy to see the metrics for each source-to-target relationship for a testrun. + +The ToDD agent does not have an opinion on the values contained in the keys or values for this JSON object, or how many k/v pairs there are - only that it is valid JSON, and is a single level (no nested objects, lists, etc). \ No newline at end of file diff --git a/docs/dsl/group-datacenter.yml b/docs/dsl/group-datacenter.yml index 1ee95b3..5a2c541 100644 --- a/docs/dsl/group-datacenter.yml +++ b/docs/dsl/group-datacenter.yml @@ -4,4 +4,4 @@ label: datacenter spec: group: datacenter matches: - - within_subnet: "172.17.0.0/16" \ No newline at end of file + - within_subnet: "172.17.0.0/16" diff --git a/docs/index.rst b/docs/index.rst index 1a17e95..e117dca 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -18,6 +18,7 @@ ToDD Documentation objects.rst resources.rst testlets.rst + customtestlets.rst This is ToDD's documentation. The docs are a work in progress, but check back soon! I'll be updating this heavily over the next few days and weeks. diff --git a/docs/testlets.rst b/docs/testlets.rst index 06afe4d..740690b 100644 --- a/docs/testlets.rst +++ b/docs/testlets.rst @@ -1,71 +1,30 @@ -ToDD Testlets +Testlets ================================ -Tests in ToDD are powered by something called "testlets". These are executable files (i.e. scripts, binaries) that accept a standard set of input, run a test application, and provide a standard set of output containing metrics from that test. +Testing applications are referred to "testlets" in ToDD. This is a handy way of referring to "whatever is actually doing the work". ToDD simply orchestrates this work. -This allows the user to simply "use" this application, and specify which agents run this application. All of the complicated stuff with respect to sending arguments to the underlying testing application as well as parsing the output, is performed inside the testlet. - -.. image:: images/testlet.png - -The testlet is actually run by the ToDD agent, so if there are 3 agents participating in a test, then 3 testlets are running. All logic that performs the test should be contained within the - -Testrun Definition ------------------- - -When you want to run a certain testlet, you refer to it by name. There are a number of testlets built in to ToDD and are therefore reserved: +There are a number of testlets built-in to the ToDD agent and are usable simply by installing the agent on a system: * http * bandwidth * ping -If you don't want to use any of the built-in testlets, you can, of course, build your own testlet (provided it follows the standard defined on this page) and refer to it by it's filename. - -Check Mode ----------- -Each testlet must support a "check mode". This is a way of running a testlet that allows the ToDD agent to know whether or not a test can be performed, without actually running the test. - -For instance, when the ToDD agent runs the "ping" testlet in check mode, it would invoke it like this: - -.. code-block:: text - - ./testletname check - -That said, the ToDD Server will distribute testrun instructions to the agents in two phases: - -* Install - run the referenced testlet in check mode, and record all params in local agent cache -* Execute - run the installed testrun instruction - -Input ------ -There is little to no similarity between various testing applications with respect to the parameters required by those applications. However, in order to simplify things for the ToDD agent, the testlet - due to it's place as a "wrapper" for a testing application - standardizes this input so the ToDD agent can invoke any testlet in a consistent manner - -.. code-block:: text - - ./testletname < target > < args > - -The ToDD agent will execute the testlet as it exists on the system, and will pass a few arguments to it (meaning the testlet must support and honor these arguments): +However, please see "Custom Testlets", and you'll find it's quite easy to build your own testlets and run them with ToDD. This extensibility was a core design principle of ToDD since the beginning of the project. -* "target" - this is always the first parameter - represents the IP address or FQDN of the target for this test instance. -* "args" - any arguments required by the underlying application. These should be passed to that application via the testlet -Output ------- -The output for every testlet is a single-level JSON object, which contains key-value pairs for the metrics gathered for that testlet. +Native Testlet Design Principles +-------------------------------- -Since the ToDD agent is responsible for executing a testlet, it is also watching stdout for the testlet to provide this JSON object. This is one of the things that make testlets a very flexible method of performing tests - since it only needs to output these metrics as JSON to stdout, the testlet can be written in any language, as long as they support the arguments described in the "Input" section. +Need a design guide outlining some requirements for native testlets: -A sample JSON object that the "ping" testlet will provide is shown below: +* Testlets must honor the "kill" channel passed to the RunTestlet function. If a "true" value is passed into that channel, the testlet must quit immediately. -.. code-block:: text +* Need to put some specifics together regarding testlets that provide some kind of "server" functionality, kind of like what you've done for custom testlets - { - "avg_latency_ms": "27.007", - "packet_loss_percentage": "0" - } +* How do args work in native testlets? It's a bit awkward to continue to use command-line style args in a native testlet but might be necessary to preserve consistency for the user. -This specific output covers the metrics for a single testlet run, which means that this is relevant to only a single target, run by a single ToDD agent. The ToDD agent will receive this output once for each target in the testrun, and submit this up to the ToDD server for collection. +* How to handle vendoring? If a testlet uses a library to run the tests, should the library get vendored with the testlet's repository, or within ToDD proper? Probably the former, but how is it used in that case? Just need to have a strategy here. (You probably vendored such libs in todd proper in order to develop them, so make sure you remove them once they're not needed) -.. NOTE:: - The ToDD Server will also aggregate each agent's report to a single metric document for the entire testrun, so that it's easy to see the metrics for each source-to-target relationship for a testrun. +* How are errors returned from the testlet logic? If a testlet returns an error, how is this handled? -The ToDD agent does not have an opinion on the values contained in the keys or values for this JSON object, or how many k/v pairs there are - only that it is valid JSON, and is a single level (no nested objects, lists, etc). \ No newline at end of file +* How does development work? Do you clone the testlet repo next to the todd repo, kind of like devstack? \ No newline at end of file diff --git a/etc/agent.cfg b/etc/agent.cfg index cfdfbdf..cca07b0 100644 --- a/etc/agent.cfg +++ b/etc/agent.cfg @@ -6,6 +6,6 @@ Port = 5672 Plugin = rabbitmq [LocalResources] -DefaultInterface = eth0 +DefaultInterface = eth2 # IPAddrOverride = 192.168.99.100 # Normally, the DefaultInterface configuration option is used to get IP address. This overrides that in the event that it doesn't work OptDir = /opt/todd/agent From 1e5f023bbaa2c4bfbaebf2599fe0b6df406429d9 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Fri, 19 Aug 2016 12:00:41 -0700 Subject: [PATCH 075/120] Updates to vagrant and make Signed-off-by: Matt Oswalt --- Makefile | 5 +++++ Vagrantfile | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d5e11f3..b0f6661 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,8 @@ build: docker build -t mierdin/todd -f Dockerfile . compile: + # Need to support some kind of mode that allows for development (i.e. not downloading the testlets live, but rather from a directory?) + # eval ./scripts/buildtestlets.sh && go install ./cmd/... ./scripts/buildtestlets.sh go install ./cmd/... @@ -42,3 +44,6 @@ configureenv: if ! [ "etc" -ef "/etc/todd" ]; then mkdir -p /etc/todd && cp -f ./etc/{agent,server}.cfg /etc/todd/; fi mkdir -p /opt/todd/{agent,server}/assets/{factcollectors,testlets} chmod -R 777 /opt/todd + + # If on Linux, enable ping testlet functionality + sudo sysctl -w net.ipv4.ping_group_range="0 12345" diff --git a/Vagrantfile b/Vagrantfile index 9317dee..a9efd37 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -6,7 +6,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.box = "trusty64" config.vm.box_url = "http://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-amd64-vagrant-disk1.box" - config.vm.synced_folder '.', '/home/vagrant/go/src/github.com/Mierdin/todd', nfs: true + config.vm.synced_folder '../../', '/home/vagrant/go/src/github.com', nfs: true config.vm.provider "virtualbox" do |v| From 9e0bddf5636610d02d35b431ce4c08d2af714933 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Fri, 19 Aug 2016 12:03:45 -0700 Subject: [PATCH 076/120] Catch up Signed-off-by: Matt Oswalt --- cmd/todd-agent/main.go | 5 ++-- docs/dsl/branch-to-dc-bw.yml | 14 +++++++++++ docs/dsl/group-branch.yml | 7 ++++++ docs/dsl/group-uraj.yml | 7 ++++++ docs/nativetests.rst | 20 ---------------- docs/roadmap.rst | 17 +++++++++++++ docs/testlets.rst | 9 +++++++ scripts/buildtestlets.sh | 46 ++++++++++++++++++++++++++---------- 8 files changed, 91 insertions(+), 34 deletions(-) create mode 100644 docs/dsl/branch-to-dc-bw.yml create mode 100644 docs/dsl/group-branch.yml create mode 100644 docs/dsl/group-uraj.yml delete mode 100644 docs/nativetests.rst create mode 100644 docs/roadmap.rst diff --git a/cmd/todd-agent/main.go b/cmd/todd-agent/main.go index c70e208..880efc7 100644 --- a/cmd/todd-agent/main.go +++ b/cmd/todd-agent/main.go @@ -25,8 +25,9 @@ import ( "github.com/Mierdin/todd/hostresources" // Testlet imports (importing these packages registers the testlets) - _ "github.com/Mierdin/todd/agent/testing/downloaded_testlets" -) + // Need to make this dynamic if possible (at compile time of course) + // TODO Not necessary anymore + //_ "github.com/toddproject/todd-nativetestlet-ping/testlet" // Command-line Arguments var arg_config string diff --git a/docs/dsl/branch-to-dc-bw.yml b/docs/dsl/branch-to-dc-bw.yml new file mode 100644 index 0000000..b1683a4 --- /dev/null +++ b/docs/dsl/branch-to-dc-bw.yml @@ -0,0 +1,14 @@ +--- +# Example test file +type: testrun +label: branch-to-dc-bw +spec: + targettype: group + source: + name: branch-uraj + app: iperf + args: "-c {{ target }}" + target: + name: datacenter + app: iperf + args: "-s" diff --git a/docs/dsl/group-branch.yml b/docs/dsl/group-branch.yml new file mode 100644 index 0000000..b1d1e82 --- /dev/null +++ b/docs/dsl/group-branch.yml @@ -0,0 +1,7 @@ +--- +type: group +label: branch-uraj +spec: + group: branch-uraj + matches: + - hostname: "uraj" diff --git a/docs/dsl/group-uraj.yml b/docs/dsl/group-uraj.yml new file mode 100644 index 0000000..b1d1e82 --- /dev/null +++ b/docs/dsl/group-uraj.yml @@ -0,0 +1,7 @@ +--- +type: group +label: branch-uraj +spec: + group: branch-uraj + matches: + - hostname: "uraj" diff --git a/docs/nativetests.rst b/docs/nativetests.rst deleted file mode 100644 index 10ec9d2..0000000 --- a/docs/nativetests.rst +++ /dev/null @@ -1,20 +0,0 @@ -Native Tests -================================ - -Need to talk about the native tests you've built in, and turn the "testlets" doc into more of a "so you want to build your own, eh?" - -Also need to figure out if you want to refer to both native and non-native as "testlets", or maybe reserve that for non-native - - - -Need a design guide outlining some requirements for native testlets: - -* Testlets must honor the "kill" channel passed to the RunTestlet function. If a "true" value is passed into that channel, the testlet must quit immediately. - -* Need to put some specifics together regarding testlets that provide some kind of "server" functionality, kind of like what you've done for custom testlets - -* How do args work in native testlets? It's a bit awkward to continue to use command-line style args in a native testlet but might be necessary to preserve consistency for the user. - -* How to handle vendoring? If a testlet uses a library to run the tests, should the library get vendored with the testlet's repository, or within ToDD proper? Probably the former, but how is it used in that case? Just need to have a strategy here. (You probably vendored such libs in todd proper in order to develop them, so make sure you remove them once they're not needed) - -* How are errors returned from the testlet logic? If a testlet returns an error, how is this handled? \ No newline at end of file diff --git a/docs/roadmap.rst b/docs/roadmap.rst new file mode 100644 index 0000000..9c9be29 --- /dev/null +++ b/docs/roadmap.rst @@ -0,0 +1,17 @@ +Roadmap +================================ + +Goals for Alpha Release + +* Store group-to-group aggreagate test data as a graph (agent groups are the lowest addressable node in teh graph) +* Consider getting rid of YAML files. It should be easy to run tests using a one-liner (native testlets should have reasonable defaults) or they just run on their own in the background +* Simplify agent setup. Is there a way to provide configuration-less agent setup? +* Need to look at modifying ToDD to run tests in a hands-off way. Think about the inspiration from the vendors at NFD12. Need to rethink the execution of tests. You know you can store test results without server connectivity, but can you run tests without server connectivity? + +Goals for Beta Release + +* Goal 1 + +Goals for Full Release + +* Web Front-End? \ No newline at end of file diff --git a/docs/testlets.rst b/docs/testlets.rst index 740690b..092742d 100644 --- a/docs/testlets.rst +++ b/docs/testlets.rst @@ -8,6 +8,9 @@ There are a number of testlets built-in to the ToDD agent and are usable simply * http * bandwidth * ping +* portknock + +These have their own separate repositories and are distributed alongside ToDD proper. They are written in Go for a number of reasons. First, it makes it easy for the testlets to honor the testlet format by leveraging some common code in the ToDD repository. However, the testlets are still their own binary. In addition, it allows ToDD to execute tests consistently across platforms (The old model of using bash scripts meant the tests had to be run on a certain platform for which that testlet knew how to parse the output) However, please see "Custom Testlets", and you'll find it's quite easy to build your own testlets and run them with ToDD. This extensibility was a core design principle of ToDD since the beginning of the project. @@ -15,6 +18,12 @@ However, please see "Custom Testlets", and you'll find it's quite easy to build Native Testlet Design Principles -------------------------------- + +Need to talk about the native tests you've built in, and turn the "testlets" doc into more of a "so you want to build your own, eh?" + +Also need to figure out if you want to refer to both native and non-native as "testlets", or maybe reserve that for non-native + + Need a design guide outlining some requirements for native testlets: * Testlets must honor the "kill" channel passed to the RunTestlet function. If a "true" value is passed into that channel, the testlet must quit immediately. diff --git a/scripts/buildtestlets.sh b/scripts/buildtestlets.sh index 2c47c0f..0875fb1 100755 --- a/scripts/buildtestlets.sh +++ b/scripts/buildtestlets.sh @@ -12,29 +12,51 @@ set -o pipefail testlets=( - 'https://github.com/Mierdin/todd-nativetestlet-ping.git' + 'github.com/toddproject/todd-nativetestlet-ping' ) -rm -rf testlettemp && mkdir testlettemp && cd testlettemp +#rm -rf testlettemp && mkdir testlettemp && cd testlettemp for i in "${testlets[@]}" do - git clone $i + #echo "Installing $i" + # git clone $i --quiet + go get -d -u $i/... done -cd .. +# cd .. + +# rm -rf agent/testing/downloaded_testlets/ && mkdir agent/testing/downloaded_testlets + +# for dir in ./testlettemp/*/ +# do +# dir=${dir%*/} +# cp testlettemp/${dir##*/}/testlet/* agent/testing/downloaded_testlets +# #echo ${dir##*/} + + + +# testletdir="$(pwd)/$dir" +# #echo $testletdir + +# ln -s $testletdir/vendor/ $testletdir/vendor/src + +# # echo ./testlettemp/todd-nativetestlet-ping + +# # Append this vendor directory to GOPATH +# # TODO need to do some cleanup somewhere to remove this +# if [[ ":$GOPATH:" != *":$testletdir/vendor:"* ]]; then +# echo "export GOPATH=$GOPATH:$testletdir/vendor" +# fi + + +# done + -rm -rf agent/testing/downloaded_testlets/ && mkdir agent/testing/downloaded_testlets -for dir in ./testlettemp/*/ -do - dir=${dir%*/} - cp testlettemp/${dir##*/}/testlet/* agent/testing/downloaded_testlets - #echo ${dir##*/} -done -rm -rf testlettemp +# rm -rf testlettemp From 5c44e3e0360b2af3058d31def3f483d881ad0b2c Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Fri, 19 Aug 2016 14:52:22 -0700 Subject: [PATCH 077/120] Got native testlets working Signed-off-by: Matt Oswalt --- agent/tasks/executetestrun.go | 176 +++++++++++++++++------------ agent/tasks/installtestrun.go | 55 ++++----- agent/testing/testlets/testlets.go | 19 ++-- cmd/todd-agent/main.go | 2 +- 4 files changed, 143 insertions(+), 109 deletions(-) diff --git a/agent/tasks/executetestrun.go b/agent/tasks/executetestrun.go index b9210de..182a14a 100644 --- a/agent/tasks/executetestrun.go +++ b/agent/tasks/executetestrun.go @@ -69,94 +69,124 @@ func (ett ExecuteTestRunTask) Run() error { // Specify size of wait group wg.Add(len(tr.Targets)) + //native := false + + // for old, newname := range testlets.nativeTestlets { + // if tr.Testlet == old { + // tr.Testlet = newname + // native = true + // break + // } + // } + + // log.Error(tr.Testlet) + // log.Error(testlets.IsNativeTestlet(tr.Testlet)) + + var testlet_path string + //if testlets.IsNativeTestlet(tr.Testlet) { + isNative, newTestletName := testlets.IsNativeTestlet(tr.Testlet) + log.Error("POOP") + log.Error(isNative) + log.Error(newTestletName) + if isNative { + + log.Error("POOP2") + + tr.Testlet = newTestletName + + log.Error(tr.Testlet) + + // Generate path to testlet and make sure it exists. + testlet_path = fmt.Sprintf("%s", tr.Testlet) + // if _, err := os.Stat(testlet_path); os.IsNotExist(err) { + // log.Error(err) + // log.Errorf("Testlet %s does not exist on this agent", tr.Testlet) + // return errors.New("Error executing testrun - testlet doesn't exist on this agent.") + // } + } else { + // Generate path to testlet and make sure it exists. + testlet_path = fmt.Sprintf("%s/assets/testlets/%s", ett.Config.LocalResources.OptDir, tr.Testlet) + if _, err := os.Stat(testlet_path); os.IsNotExist(err) { + log.Errorf("Testlet %s does not exist on this agent", tr.Testlet) + return errors.New("Error executing testrun - testlet doesn't exist on this agent.") + } + } + // Execute testlets against all targets asynchronously for i := range tr.Targets { thisTarget := tr.Targets[i] - if testlets.IsNativeTestlet(tr.Testlet) { - go func() { + go func() { - // TODO(mierdin): Something worried me here (can't remember what) regarding - // if only some agents were running native testlets, does this wait group methodology work? - // Sorry it's not clear, I have had a bit too much wine. - defer wg.Done() + defer wg.Done() - nativeTestlet, err := testlets.NewTestlet(tr.Testlet) - if err != nil { - //TODO(mierdin) do something - } - - metrics, err := nativeTestlet.Run("8.8.8.8", []string{"-c 10", "-s"}, ett.TimeLimit) - //log.Error(nativeTestlet.RunFunction) - if err != nil { - log.Errorf("Testlet completed with error '%s'", err) - gatheredData[thisTarget] = "error" - } + log.Debugf("Full testlet command and args: '%s %s %s'", tr.Testlet, thisTarget, tr.Args) + cmd := exec.Command(tr.Testlet, thisTarget, tr.Args) - // The metrics infrastructure requires that we collect metrics as a JSON string - // (which is a result of building non-native testlets in early versions of ToDD) - // So let's convert, and add to gatheredData - metrics_json, err := json.Marshal(metrics) - if err != nil { - //TODO(mierdin) do something - } - gatheredData[thisTarget] = string(metrics_json) + // Stdout buffer + cmdOutput := &bytes.Buffer{} + // Attach buffer to command + cmd.Stdout = cmdOutput - }() - } else { - // Generate path to testlet and make sure it exists. - testlet_path := fmt.Sprintf("%s/assets/testlets/%s", ett.Config.LocalResources.OptDir, tr.Testlet) - if _, err := os.Stat(testlet_path); os.IsNotExist(err) { - log.Errorf("Testlet %s does not exist on this agent", tr.Testlet) - return errors.New("Error executing testrun - testlet doesn't exist on this agent.") - } + // Execute collector + cmd.Start() + // TODO(mierdin): Does this need to be a buffered channel? + done := make(chan error, 1) go func() { + done <- cmd.Wait() + }() - defer wg.Done() - - log.Debugf("Full testlet command and args: '%s %s %s'", testlet_path, thisTarget, tr.Args) - cmd := exec.Command(testlet_path, thisTarget, tr.Args) - - // Stdout buffer - cmdOutput := &bytes.Buffer{} - // Attach buffer to command - cmd.Stdout = cmdOutput - - // Execute collector - cmd.Start() - - // TODO(mierdin): Does this need to be a buffered channel? - done := make(chan error, 1) - go func() { - done <- cmd.Wait() - }() - - // This select statement will block until one of these two conditions are met: - // - The testlet finishes, in which case the channel "done" will be receive a value - // - The configured time limit is exceeded (expected for testlets running in server mode) - select { - case <-time.After(time.Duration(ett.TimeLimit) * time.Second): - if err := cmd.Process.Kill(); err != nil { - log.Errorf("Failed to kill %s after timeout: %s", testlet_path, err) - } else { - log.Debug("Successfully killed ", testlet_path) - } - case err := <-done: - if err != nil { - log.Errorf("Testlet %s completed with error '%s'", testlet_path, err) - gatheredData[thisTarget] = "error" - } else { - log.Debugf("Testlet %s completed without error", testlet_path) - } + // This select statement will block until one of these two conditions are met: + // - The testlet finishes, in which case the channel "done" will be receive a value + // - The configured time limit is exceeded (expected for testlets running in server mode) + select { + case <-time.After(time.Duration(ett.TimeLimit) * time.Second): + if err := cmd.Process.Kill(); err != nil { + log.Errorf("Failed to kill %s after timeout: %s", testlet_path, err) + } else { + log.Debug("Successfully killed ", testlet_path) } + case err := <-done: + if err != nil { + log.Errorf("Testlet %s completed with error '%s'", testlet_path, err) + gatheredData[thisTarget] = "error" + } else { + log.Debugf("Testlet %s completed without error", testlet_path) + } + } - // Record test data - gatheredData[thisTarget] = string(cmdOutput.Bytes()) + // Record test data + gatheredData[thisTarget] = string(cmdOutput.Bytes()) + // // TODO(mierdin): Something worried me here (can't remember what) regarding + // // if only some agents were running native testlets, does this wait group methodology work? + // // Sorry it's not clear, I have had a bit too much wine. + // defer wg.Done() + + // nativeTestlet, err := testlets.NewTestlet(tr.Testlet) + // if err != nil { + // //TODO(mierdin) do something + // } + + // metrics, err := nativeTestlet.Run("8.8.8.8", []string{"-c 10", "-s"}, ett.TimeLimit) + // //log.Error(nativeTestlet.RunFunction) + // if err != nil { + // log.Errorf("Testlet completed with error '%s'", err) + // gatheredData[thisTarget] = "error" + // } + + // // The metrics infrastructure requires that we collect metrics as a JSON string + // // (which is a result of building non-native testlets in early versions of ToDD) + // // So let's convert, and add to gatheredData + // metrics_json, err := json.Marshal(metrics) + // if err != nil { + // //TODO(mierdin) do something + // } + // gatheredData[thisTarget] = string(metrics_json) + + }() - }() - } } wg.Wait() diff --git a/agent/tasks/installtestrun.go b/agent/tasks/installtestrun.go index 131df4d..96bb662 100644 --- a/agent/tasks/installtestrun.go +++ b/agent/tasks/installtestrun.go @@ -43,48 +43,49 @@ func (itt InstallTestRunTask) Run() error { return errors.New("Testlet parameter for this testrun is null") } - // Determine if this is a valid native testlet - _, err := testlets.NewTestlet(itt.Tr.Testlet) + var testlet_path string + // Determine if this is a valid native testlet + //_, err := testlets.NewTestlet(itt.Tr.Testlet) + isNative, newName := testlets.IsNativeTestlet(itt.Tr.Testlet) // Not a native testlet - attempt to run check mode on testlet in filesystem - if err != nil { + if isNative { + // Nothing to do, as we're using a native testlet + log.Infof("%s is a native testlet - installing testrun.", itt.Tr.Testlet) + //itt.Tr.Testlet = newName + testlet_path = newName + } else { // Generate path to testlet and make sure it exists. - testlet_path := fmt.Sprintf("%s/assets/testlets/%s", itt.Config.LocalResources.OptDir, itt.Tr.Testlet) + testlet_path = fmt.Sprintf("%s/assets/testlets/%s", itt.Config.LocalResources.OptDir, itt.Tr.Testlet) if _, err := os.Stat(testlet_path); os.IsNotExist(err) { log.Errorf("Testlet %s does not exist on this agent", itt.Tr.Testlet) return errors.New("Error installing testrun - testlet doesn't exist on this agent.") } + } - // Run the testlet in check mode to verify that everything is okay to run this test - log.Debug("Running testlet in check mode: ", testlet_path) - cmd := exec.Command(testlet_path, "check") - - // Stdout buffer - cmdOutput := &bytes.Buffer{} - // Attach buffer to command - cmd.Stdout = cmdOutput - // Execute collector - cmd.Run() - - // This is probably the best cross-platform way to see if check mode passed. - if strings.Contains(string(cmdOutput.Bytes()), "Check mode PASSED") { - log.Debugf("Check mode for %s passed", testlet_path) - } else { - log.Error("Testlet returned an error during check mode: ", string(cmdOutput.Bytes())) - return errors.New("Testlet returned an error during check mode") - } - - } else { + // Run the testlet in check mode to verify that everything is okay to run this test + log.Debug("Running testlet in check mode: ", testlet_path) + cmd := exec.Command(testlet_path, "check") - // Nothing to do, as we're using a native testlet - log.Infof("%s is a native testlet - installing testrun.", itt.Tr.Testlet) + // Stdout buffer + cmdOutput := &bytes.Buffer{} + // Attach buffer to command + cmd.Stdout = cmdOutput + // Execute collector + cmd.Run() + // This is probably the best cross-platform way to see if check mode passed. + if strings.Contains(string(cmdOutput.Bytes()), "Check mode PASSED") { + log.Debugf("Check mode for %s passed", testlet_path) + } else { + log.Error("Testlet returned an error during check mode: ", string(cmdOutput.Bytes())) + return errors.New("Testlet returned an error during check mode") } // Insert testrun into agent cache var ac = cache.NewAgentCache(itt.Config) - err = ac.InsertTestRun(itt.Tr) + err := ac.InsertTestRun(itt.Tr) if err != nil { log.Error(err) return errors.New("Problem installing test run into agent cache") diff --git a/agent/testing/testlets/testlets.go b/agent/testing/testlets/testlets.go index 4458892..7bcafe1 100644 --- a/agent/testing/testlets/testlets.go +++ b/agent/testing/testlets/testlets.go @@ -12,10 +12,13 @@ import ( ) var ( - testletsMu sync.RWMutex - testlets = make(map[string]Testlet) - done = make(chan error) // Used from within the goroutine to inform the infrastructure it has finished - kill = make(chan bool) // Used from outside the goroutine to inform the goroutine to stop + testletsMu sync.RWMutex + testlets = make(map[string]Testlet) + done = make(chan error) // Used from within the goroutine to inform the infrastructure it has finished + kill = make(chan bool) // Used from outside the goroutine to inform the goroutine to stop + nativeTestlets = map[string]string{ + "ping": "toddping", + } ) // Testlet defines what a testlet should look like if built in native @@ -107,11 +110,11 @@ func (b BaseTestlet) Kill() error { // IsNativeTestlet polls the list of registered native testlets, and returns // true if the referenced name exists -func IsNativeTestlet(name string) bool { - if _, ok := testlets[name]; ok { - return true +func IsNativeTestlet(name string) (bool, string) { + if _, ok := nativeTestlets[name]; ok { + return true, nativeTestlets[name] } else { - return false + return false, "" } } diff --git a/cmd/todd-agent/main.go b/cmd/todd-agent/main.go index 880efc7..a7f527e 100644 --- a/cmd/todd-agent/main.go +++ b/cmd/todd-agent/main.go @@ -23,11 +23,11 @@ import ( "github.com/Mierdin/todd/comms" "github.com/Mierdin/todd/config" "github.com/Mierdin/todd/hostresources" - // Testlet imports (importing these packages registers the testlets) // Need to make this dynamic if possible (at compile time of course) // TODO Not necessary anymore //_ "github.com/toddproject/todd-nativetestlet-ping/testlet" +) // Command-line Arguments var arg_config string From 43f330997f42cdc112ed4377bcdd024b819cb9c7 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Mon, 22 Aug 2016 23:39:44 -0700 Subject: [PATCH 078/120] Catch up Signed-off-by: Matt Oswalt --- Makefile | 9 ++- agent/tasks/executetestrun.go | 79 ++++----------------- agent/tasks/installtestrun.go | 11 ++- agent/testing/bashtestlets/notatallping | 32 --------- agent/testing/testlets/testlets.go | 34 +++++++-- scripts/buildtestlets.sh | 91 ------------------------- scripts/gettestlets.sh | 23 +++++++ 7 files changed, 77 insertions(+), 202 deletions(-) delete mode 100755 agent/testing/bashtestlets/notatallping delete mode 100755 scripts/buildtestlets.sh create mode 100755 scripts/gettestlets.sh diff --git a/Makefile b/Makefile index b0f6661..adb170b 100644 --- a/Makefile +++ b/Makefile @@ -11,9 +11,12 @@ build: docker build -t mierdin/todd -f Dockerfile . compile: - # Need to support some kind of mode that allows for development (i.e. not downloading the testlets live, but rather from a directory?) - # eval ./scripts/buildtestlets.sh && go install ./cmd/... - ./scripts/buildtestlets.sh + # TODO(mierdin): Need to support some kind of mode that allows for development (i.e. not downloading the testlets live, but rather from a directory?) + + # Installing testlets + ./scripts/gettestlets.sh + + # Installing ToDD go install ./cmd/... install: configureenv diff --git a/agent/tasks/executetestrun.go b/agent/tasks/executetestrun.go index 182a14a..de1ebbc 100644 --- a/agent/tasks/executetestrun.go +++ b/agent/tasks/executetestrun.go @@ -52,8 +52,9 @@ func (ett ExecuteTestRunTask) Run() error { gatheredData = map[string]string{} // Waiting three seconds to ensure all the agents have their tasks before we potentially hammer the network - // TODO(mierdin): This is a bit of a copout. I would like to do something a little more robust than simply waiting - // for a few seconds in the future. + // TODO(mierdin): This is a temporary measure - in the future, testruns will be executed via time schedule, + // making not only this sleep, but also the entire task unnecessary. Testruns will simply be installed, and + // executed when the time is right. time.Sleep(3000 * time.Millisecond) // Retrieve test from cache by UUID @@ -66,52 +67,29 @@ func (ett ExecuteTestRunTask) Run() error { log.Debugf("IMMA FIRIN MAH LAZER (for test %s) ", ett.TestUuid) - // Specify size of wait group + // Specify size of wait group equal to number of targets wg.Add(len(tr.Targets)) - //native := false - - // for old, newname := range testlets.nativeTestlets { - // if tr.Testlet == old { - // tr.Testlet = newname - // native = true - // break - // } - // } - - // log.Error(tr.Testlet) - // log.Error(testlets.IsNativeTestlet(tr.Testlet)) - var testlet_path string - //if testlets.IsNativeTestlet(tr.Testlet) { isNative, newTestletName := testlets.IsNativeTestlet(tr.Testlet) - log.Error("POOP") - log.Error(isNative) - log.Error(newTestletName) - if isNative { - - log.Error("POOP2") - - tr.Testlet = newTestletName - log.Error(tr.Testlet) - - // Generate path to testlet and make sure it exists. - testlet_path = fmt.Sprintf("%s", tr.Testlet) - // if _, err := os.Stat(testlet_path); os.IsNotExist(err) { - // log.Error(err) - // log.Errorf("Testlet %s does not exist on this agent", tr.Testlet) - // return errors.New("Error executing testrun - testlet doesn't exist on this agent.") - // } + // If we're running a native testlet, we want testlet_path to simply be the testlet name + // (since it is a requirement that the native-Go testlets are in the PATH) + // If the testlet is not native, we can get the full path. + if isNative { + testlet_path = newTestletName } else { // Generate path to testlet and make sure it exists. testlet_path = fmt.Sprintf("%s/assets/testlets/%s", ett.Config.LocalResources.OptDir, tr.Testlet) if _, err := os.Stat(testlet_path); os.IsNotExist(err) { - log.Errorf("Testlet %s does not exist on this agent", tr.Testlet) + log.Errorf("Testlet %s does not exist on this agent", testlet_path) return errors.New("Error executing testrun - testlet doesn't exist on this agent.") } } + // TODO(mierdin): What about testlets running as servers (i.e. 'iperf -s')? Are we spinning up len(tr.Targets) + // number of those? + // Execute testlets against all targets asynchronously for i := range tr.Targets { @@ -121,8 +99,8 @@ func (ett ExecuteTestRunTask) Run() error { defer wg.Done() - log.Debugf("Full testlet command and args: '%s %s %s'", tr.Testlet, thisTarget, tr.Args) - cmd := exec.Command(tr.Testlet, thisTarget, tr.Args) + log.Debugf("Full testlet command and args: '%s %s %s'", testlet_path, thisTarget, tr.Args) + cmd := exec.Command(testlet_path, thisTarget, tr.Args) // Stdout buffer cmdOutput := &bytes.Buffer{} @@ -159,34 +137,7 @@ func (ett ExecuteTestRunTask) Run() error { // Record test data gatheredData[thisTarget] = string(cmdOutput.Bytes()) - // // TODO(mierdin): Something worried me here (can't remember what) regarding - // // if only some agents were running native testlets, does this wait group methodology work? - // // Sorry it's not clear, I have had a bit too much wine. - // defer wg.Done() - - // nativeTestlet, err := testlets.NewTestlet(tr.Testlet) - // if err != nil { - // //TODO(mierdin) do something - // } - - // metrics, err := nativeTestlet.Run("8.8.8.8", []string{"-c 10", "-s"}, ett.TimeLimit) - // //log.Error(nativeTestlet.RunFunction) - // if err != nil { - // log.Errorf("Testlet completed with error '%s'", err) - // gatheredData[thisTarget] = "error" - // } - - // // The metrics infrastructure requires that we collect metrics as a JSON string - // // (which is a result of building non-native testlets in early versions of ToDD) - // // So let's convert, and add to gatheredData - // metrics_json, err := json.Marshal(metrics) - // if err != nil { - // //TODO(mierdin) do something - // } - // gatheredData[thisTarget] = string(metrics_json) - }() - } wg.Wait() diff --git a/agent/tasks/installtestrun.go b/agent/tasks/installtestrun.go index 96bb662..355e45e 100644 --- a/agent/tasks/installtestrun.go +++ b/agent/tasks/installtestrun.go @@ -45,16 +45,15 @@ func (itt InstallTestRunTask) Run() error { var testlet_path string - // Determine if this is a valid native testlet - //_, err := testlets.NewTestlet(itt.Tr.Testlet) + // Determine if this is a native testlet isNative, newName := testlets.IsNativeTestlet(itt.Tr.Testlet) - // Not a native testlet - attempt to run check mode on testlet in filesystem + + // If we're running a native testlet, we want testlet_path to simply be the testlet name + // (since it is a requirement that the native-Go testlets are in the PATH) + // If the testlet is not native, we can get the full path. if isNative { - // Nothing to do, as we're using a native testlet log.Infof("%s is a native testlet - installing testrun.", itt.Tr.Testlet) - //itt.Tr.Testlet = newName testlet_path = newName - } else { // Generate path to testlet and make sure it exists. testlet_path = fmt.Sprintf("%s/assets/testlets/%s", itt.Config.LocalResources.OptDir, itt.Tr.Testlet) diff --git a/agent/testing/bashtestlets/notatallping b/agent/testing/bashtestlets/notatallping deleted file mode 100755 index aed17d4..0000000 --- a/agent/testing/bashtestlets/notatallping +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env bash - -# Copyright 2016 Matt Oswalt. Use or modification of this -# source code is governed by the license provided here: -# https://github.com/Mierdin/todd/blob/master/LICENSE - -check() { - echo "Running in check mode" - command -v ping >/dev/null 2>&1 || { echo >&2 "Ping is not installed. Exiting."; exit 1; } - echo "Check mode PASSED" - exit 0 -} - -if [[ $1 == "check" ]]; - then - check -fi - -# The first argument after the testlet name will ALWAYS be the target. So, $1 is always the target, and all arguments from $2 and up should be passed into the app command -app_args="$@" -app_args=${app_args#$0 } -app_args=${app_args#$1 } - -app_run_command="ping $app_args $1" - -output=$($app_run_command) - -packet_loss=$(echo $output | tr '\n' ' ' | sed -n 's/.* \(.*\)% packet loss.*/\1/p') -avg_latency=$(echo $output | tr '\n' ' ' | sed -n 's/.*dev = .*\/\(.*\)\/.*\/.*/\1/p') - -teslet_data='{"packet_loss_percentage":"'$packet_loss'", "avg_latency_ms":"'$avg_latency'"}' -echo $teslet_data \ No newline at end of file diff --git a/agent/testing/testlets/testlets.go b/agent/testing/testlets/testlets.go index 7bcafe1..ea35bce 100644 --- a/agent/testing/testlets/testlets.go +++ b/agent/testing/testlets/testlets.go @@ -6,16 +6,37 @@ import ( "sort" "sync" "time" - //"sync/atomic" log "github.com/Sirupsen/logrus" ) +// NOTE +// +// Early efforts to build native-Go testlets involved the embedding of testlet logic into the +// ToDD agent itself. As a result, it was important to build some reusable infrastructure so that goroutines +// running testlet code inside the agent could be controlled, and that new testlets could benefit from this +// infrastructure. +// +// Since then, the decision was made to keep testlets as their own separate binaries. Despite this, we can still benefit +// from having them in Go because it is much more cross-platform than bash scripts. +// +// These testlets are in their own repositories, and they do actually use some of the logic below, just not as meaningfully +// and comprehensively as they would have if they were baked in to the agent. Those testlets will still vendor this code +// and leverage the "Testlet" interface so that in the future, if we want to roll these into the todd-agent, those +// testlets will already conform to the standard provided below. + var ( - testletsMu sync.RWMutex - testlets = make(map[string]Testlet) - done = make(chan error) // Used from within the goroutine to inform the infrastructure it has finished - kill = make(chan bool) // Used from outside the goroutine to inform the goroutine to stop + testletsMu sync.RWMutex + testlets = make(map[string]Testlet) + done = make(chan error) // Used from within the goroutine to inform the infrastructure it has finished + kill = make(chan bool) // Used from outside the goroutine to inform the goroutine to stop + + // This map provides name redirection so that the native testlets can use names that don't + // conflict with existing system tools (i.e. using "toddping" instead of "ping") but users + // can still refer to the testlets using simple names. + // + // In short, users refer to the testlet by and this map will redirect to the + // actual binary name nativeTestlets = map[string]string{ "ping": "toddping", } @@ -73,7 +94,8 @@ func (b BaseTestlet) Run(target string, args []string, timeLimit int) (map[strin // kill = make(chan bool) // TODO(mierdin): Based on experimentation, this will keep running even if this function returns. - // Need to be sure about how this ends. Also might want to evaluate the same for the existing non-native model, likely has the same issue + // Need to be sure about how this ends. Also might want to evaluate the same for the existing + // non-native model, likely has the same issue go func() { theseMetrics, err := b.RunFunction(target, args, kill) metrics = theseMetrics //TODO(mierdin): Gross. diff --git a/scripts/buildtestlets.sh b/scripts/buildtestlets.sh deleted file mode 100755 index 0875fb1..0000000 --- a/scripts/buildtestlets.sh +++ /dev/null @@ -1,91 +0,0 @@ -#!/bin/bash - -# Copyright 2016 Matt Oswalt. Use or modification of this -# source code is governed by the license provided here: -# https://github.com/mierdin/todd/blob/master/LICENSE - -# This script downloads ToDD testlets prior to compile - -set -e -set -u -set -o pipefail - - -testlets=( - 'github.com/toddproject/todd-nativetestlet-ping' - ) - - -#rm -rf testlettemp && mkdir testlettemp && cd testlettemp - -for i in "${testlets[@]}" -do - #echo "Installing $i" - # git clone $i --quiet - go get -d -u $i/... -done - -# cd .. - -# rm -rf agent/testing/downloaded_testlets/ && mkdir agent/testing/downloaded_testlets - -# for dir in ./testlettemp/*/ -# do -# dir=${dir%*/} -# cp testlettemp/${dir##*/}/testlet/* agent/testing/downloaded_testlets -# #echo ${dir##*/} - - - -# testletdir="$(pwd)/$dir" -# #echo $testletdir - -# ln -s $testletdir/vendor/ $testletdir/vendor/src - -# # echo ./testlettemp/todd-nativetestlet-ping - -# # Append this vendor directory to GOPATH -# # TODO need to do some cleanup somewhere to remove this -# if [[ ":$GOPATH:" != *":$testletdir/vendor:"* ]]; then -# echo "export GOPATH=$GOPATH:$testletdir/vendor" -# fi - - -# done - - - - -# rm -rf testlettemp - - - -# rebuild plugins: -# _debug "removing: ${plugin_dir:?}/*" -# rm -rf "${plugin_dir:?}/"* -# mkdir -p "${plugin_dir}" - -# _info "building plugins" -# find "${__proj_dir}/plugin/" -type d -iname "snap-*" -print0 | xargs -0 -n 1 -I{} "${__dir}/build_plugin.sh" {} - -#--------- - - - -# __dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -# __proj_dir="$(dirname "$__dir")" - -# # shellcheck source=scripts/common.sh -# . "${__dir}/common.sh" - -# build_dir="${__proj_dir}/build" -# plugin_dir="${build_dir}/plugin" - -# plugin_src_path=$1 -# plugin_name=$(basename "${plugin_src_path}") -# go_build=(go build -a -ldflags "-w") - -# _debug "plugin source: ${plugin_src_path}" -# _info "building ${plugin_name}" - -# (cd "${plugin_src_path}" && "${go_build[@]}" -o "${plugin_dir}/${plugin_name}" . || exit 1) \ No newline at end of file diff --git a/scripts/gettestlets.sh b/scripts/gettestlets.sh new file mode 100755 index 0000000..c8e56ac --- /dev/null +++ b/scripts/gettestlets.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +# Copyright 2016 Matt Oswalt. Use or modification of this +# source code is governed by the license provided here: +# https://github.com/mierdin/todd/blob/master/LICENSE + +# This script installs ToDD-native testlets + +set -e +set -u +set -o pipefail + +# Install these testlets - comment out specific testlets to +# control what's installed +testlets=( + 'github.com/toddproject/todd-nativetestlet-ping' +) + +for i in "${testlets[@]}" +do + echo "Installing $i" + go get -u $i/cmd/... +done From ca3352b01faf11237a45a3e8db6589138a847924 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Mon, 22 Aug 2016 23:43:58 -0700 Subject: [PATCH 079/120] Added note about future development efforts around testlets Signed-off-by: Matt Oswalt --- Makefile | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index adb170b..ac80285 100644 --- a/Makefile +++ b/Makefile @@ -11,8 +11,11 @@ build: docker build -t mierdin/todd -f Dockerfile . compile: - # TODO(mierdin): Need to support some kind of mode that allows for development (i.e. not downloading the testlets live, but rather from a directory?) - + # TODO(mierdin): Need to support some kind of mode that allows for development. + # The current gettestlets.sh script downloads the testlets from Github, meaning + # a developer would already have to have changes pushed to those repos' master + # Looking for something like devstack, etc. + # # Installing testlets ./scripts/gettestlets.sh From 89ea72a6c9ba8c3369d4d7ec790dddfd6a559c86 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Mon, 22 Aug 2016 23:51:29 -0700 Subject: [PATCH 080/120] Some fixes from reviewing the PR Signed-off-by: Matt Oswalt --- .gitignore | 1 - Vagrantfile | 2 +- agent/tasks/executetestrun.go | 21 ++++++++------------- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index 9896fa8..925635d 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,3 @@ preso_secret/ client/repeattest.sh *.out *.DS_Store* -agent/testing/downloaded_testlets/ \ No newline at end of file diff --git a/Vagrantfile b/Vagrantfile index a9efd37..9317dee 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -6,7 +6,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.box = "trusty64" config.vm.box_url = "http://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-amd64-vagrant-disk1.box" - config.vm.synced_folder '../../', '/home/vagrant/go/src/github.com', nfs: true + config.vm.synced_folder '.', '/home/vagrant/go/src/github.com/Mierdin/todd', nfs: true config.vm.provider "virtualbox" do |v| diff --git a/agent/tasks/executetestrun.go b/agent/tasks/executetestrun.go index de1ebbc..90776bb 100644 --- a/agent/tasks/executetestrun.go +++ b/agent/tasks/executetestrun.go @@ -25,16 +25,6 @@ import ( "github.com/Mierdin/todd/config" ) -var ( - // gatheredData represents test data from this agent for all targets. - // Key is target name, value is JSON output from testlet for that target - // This is reset to a blank map every time ExecuteTestRunTask is called - gatheredData = make(map[string]string) - - // Use a wait group to ensure that all of the testlets have a chance to finish - wg sync.WaitGroup -) - // ExecuteTestRunTask defines this particular task. type ExecuteTestRunTask struct { BaseTask @@ -48,8 +38,13 @@ type ExecuteTestRunTask struct { // a testrun will be executed once per target, all in parallel. func (ett ExecuteTestRunTask) Run() error { - // Make sure we're working with a clean slate - gatheredData = map[string]string{} + // gatheredData represents test data from this agent for all targets. + // Key is target name, value is JSON output from testlet for that target + // This is reset to a blank map every time ExecuteTestRunTask is called + gatheredData := map[string]string{} + + // Use a wait group to ensure that all of the testlets have a chance to finish + var wg sync.WaitGroup // Waiting three seconds to ensure all the agents have their tasks before we potentially hammer the network // TODO(mierdin): This is a temporary measure - in the future, testruns will be executed via time schedule, @@ -110,7 +105,7 @@ func (ett ExecuteTestRunTask) Run() error { // Execute collector cmd.Start() - // TODO(mierdin): Does this need to be a buffered channel? + // TODO(mierdin): Why is this a buffered channel? Is this necessary? done := make(chan error, 1) go func() { done <- cmd.Wait() From 25b64d535513a8aa3655567b5f84d4230d95ff2a Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Tue, 23 Aug 2016 22:43:25 -0700 Subject: [PATCH 081/120] Moved testlets outside to the testing package Signed-off-by: Matt Oswalt --- agent/testing/{testlets => }/testlets.go | 48 ++++++++++++------------ 1 file changed, 23 insertions(+), 25 deletions(-) rename agent/testing/{testlets => }/testlets.go (79%) diff --git a/agent/testing/testlets/testlets.go b/agent/testing/testlets.go similarity index 79% rename from agent/testing/testlets/testlets.go rename to agent/testing/testlets.go index ea35bce..dc4bb51 100644 --- a/agent/testing/testlets/testlets.go +++ b/agent/testing/testlets.go @@ -1,4 +1,12 @@ -package testlets +/* + ToDD task - set keyvalue pair in cache + + Copyright 2016 Matt Oswalt. Use or modification of this + source code is governed by the license provided here: + https://github.com/Mierdin/todd/blob/master/LICENSE +*/ + +package testing import ( "errors" @@ -17,13 +25,14 @@ import ( // running testlet code inside the agent could be controlled, and that new testlets could benefit from this // infrastructure. // -// Since then, the decision was made to keep testlets as their own separate binaries. Despite this, we can still benefit -// from having them in Go because it is much more cross-platform than bash scripts. +// Since then, the decision was made to keep testlets as their own separate binaries. // // These testlets are in their own repositories, and they do actually use some of the logic below, just not as meaningfully -// and comprehensively as they would have if they were baked in to the agent. Those testlets will still vendor this code -// and leverage the "Testlet" interface so that in the future, if we want to roll these into the todd-agent, those -// testlets will already conform to the standard provided below. +// and comprehensively as they would have if they were baked in to the agent. The development standard for all "blessed" +// testlets will still ensure that they use this interface, so that if we decide to bake them into the agent in the future, +// they'll already conform. +// +// (The vast majority of this code was inspired by the database drivers implementation in the stdlib) var ( testletsMu sync.RWMutex @@ -69,7 +78,6 @@ type Testlet interface { // without worrying about things like managing goroutines or channels. That's all // managed by the "Run" or "Kill" functions RunTestlet(string, []string, chan bool) (map[string]string, error) - // TODO(mierdin): is this really the best name for it? Maybe something that's less confusing, less like "Run" // All testlets must be able to stop operation when sent a Kill command. Kill() error @@ -89,16 +97,12 @@ func (b BaseTestlet) Run(target string, args []string, timeLimit int) (map[strin var metrics map[string]string - // TODO(mierdin): ensure channel is nil - // done = make(chan error) - // kill = make(chan bool) + // Ensure control channels are empty + done := make(chan error) + kill := make(chan bool) - // TODO(mierdin): Based on experimentation, this will keep running even if this function returns. - // Need to be sure about how this ends. Also might want to evaluate the same for the existing - // non-native model, likely has the same issue go func() { - theseMetrics, err := b.RunFunction(target, args, kill) - metrics = theseMetrics //TODO(mierdin): Gross. + metrics, err := b.RunFunction(target, args, kill) done <- err }() @@ -112,7 +116,7 @@ func (b BaseTestlet) Run(target string, args []string, timeLimit int) (map[strin case err := <-done: if err != nil { - return map[string]string{}, errors.New("testlet error") // TODO(mierdin): elaborate? + return map[string]string{}, errors.New("testlet error") } else { log.Debugf("Testlet completed without error") return metrics, nil @@ -120,13 +124,10 @@ func (b BaseTestlet) Run(target string, args []string, timeLimit int) (map[strin } } +// Kill is currently unimplemented. This will have to be coordinated with "Run". Basically +// you need a way to kill this testlet (and that's really only possible when running +// async) Probably just want to set the channel to something so the select within "Run" will execute func (b BaseTestlet) Kill() error { - // TODO (mierdin): This will have to be coordinated with the task above. Basically - // you need a way to kill this testlet (and that's really only possible when running - // async) - - // Probably just want to set the channel to something so the select within "Run" will execute - return nil } @@ -144,9 +145,6 @@ func IsNativeTestlet(name string) (bool, string) { func NewTestlet(name string) (Testlet, error) { if testlet, ok := testlets[name]; ok { - - // testlet.runFunction = testlet.run - return testlet, nil } else { return nil, errors.New( From e632a0487be7e3f10190094aac53c106830093ea Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Sun, 18 Sep 2016 02:06:28 -0700 Subject: [PATCH 082/120] Updated testlets.go Signed-off-by: Matt Oswalt --- agent/testing/testlets.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agent/testing/testlets.go b/agent/testing/testlets.go index dc4bb51..17baf33 100644 --- a/agent/testing/testlets.go +++ b/agent/testing/testlets.go @@ -6,7 +6,7 @@ https://github.com/Mierdin/todd/blob/master/LICENSE */ -package testing +package testlets import ( "errors" From fdf257119e9918d752f2554b89cb6e113d5c1b8a Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Mon, 19 Sep 2016 00:56:17 -0700 Subject: [PATCH 083/120] Various moves Signed-off-by: Matt Oswalt --- agent/testing/{reporting.go => testing.go} | 0 agent/testing/testlets.go | 189 --------------------- 2 files changed, 189 deletions(-) rename agent/testing/{reporting.go => testing.go} (100%) delete mode 100644 agent/testing/testlets.go diff --git a/agent/testing/reporting.go b/agent/testing/testing.go similarity index 100% rename from agent/testing/reporting.go rename to agent/testing/testing.go diff --git a/agent/testing/testlets.go b/agent/testing/testlets.go deleted file mode 100644 index 17baf33..0000000 --- a/agent/testing/testlets.go +++ /dev/null @@ -1,189 +0,0 @@ -/* - ToDD task - set keyvalue pair in cache - - Copyright 2016 Matt Oswalt. Use or modification of this - source code is governed by the license provided here: - https://github.com/Mierdin/todd/blob/master/LICENSE -*/ - -package testlets - -import ( - "errors" - "fmt" - "sort" - "sync" - "time" - - log "github.com/Sirupsen/logrus" -) - -// NOTE -// -// Early efforts to build native-Go testlets involved the embedding of testlet logic into the -// ToDD agent itself. As a result, it was important to build some reusable infrastructure so that goroutines -// running testlet code inside the agent could be controlled, and that new testlets could benefit from this -// infrastructure. -// -// Since then, the decision was made to keep testlets as their own separate binaries. -// -// These testlets are in their own repositories, and they do actually use some of the logic below, just not as meaningfully -// and comprehensively as they would have if they were baked in to the agent. The development standard for all "blessed" -// testlets will still ensure that they use this interface, so that if we decide to bake them into the agent in the future, -// they'll already conform. -// -// (The vast majority of this code was inspired by the database drivers implementation in the stdlib) - -var ( - testletsMu sync.RWMutex - testlets = make(map[string]Testlet) - done = make(chan error) // Used from within the goroutine to inform the infrastructure it has finished - kill = make(chan bool) // Used from outside the goroutine to inform the goroutine to stop - - // This map provides name redirection so that the native testlets can use names that don't - // conflict with existing system tools (i.e. using "toddping" instead of "ping") but users - // can still refer to the testlets using simple names. - // - // In short, users refer to the testlet by and this map will redirect to the - // actual binary name - nativeTestlets = map[string]string{ - "ping": "toddping", - } -) - -// Testlet defines what a testlet should look like if built in native -// go and compiled with the agent -type Testlet interface { - - // Run is the "workflow" function for a testlet. It handles running - // the RunTestlet function asynchronously and managing the state therein. - // - // Params are - // target (string) - // args ([]string) - // timeLimit (int in seconds) - // - // Returns: - // metrics (map[string]interface{}) - // (name of metric is key, value is metric value) - // - // Keep as much logic out of here as possible. All native testlets - // must support a "Kill" method, so it's best to implement core testlet - // logic in a separate function so that the Run and Kill commands can manage - // execution of that logic in a goroutine - Run(string, []string, int) (map[string]string, error) - - // RunTestlet is designed to be the one-stop shop for testlet logic. - // The developer of a native testlet just needs to implement the testlet logic here, - // without worrying about things like managing goroutines or channels. That's all - // managed by the "Run" or "Kill" functions - RunTestlet(string, []string, chan bool) (map[string]string, error) - - // All testlets must be able to stop operation when sent a Kill command. - Kill() error -} - -type rtfunc func(target string, args []string, kill chan bool) (map[string]string, error) - -type BaseTestlet struct { - - // rtfunc is a type that will store our RunTestlet function. It is the responsibility - // of the "child" testlet to set this value upon creation - RunFunction rtfunc -} - -// Run takes care of running the testlet function and managing it's operation given the parameters provided -func (b BaseTestlet) Run(target string, args []string, timeLimit int) (map[string]string, error) { - - var metrics map[string]string - - // Ensure control channels are empty - done := make(chan error) - kill := make(chan bool) - - go func() { - metrics, err := b.RunFunction(target, args, kill) - done <- err - }() - - // This select statement will block until one of these two conditions are met: - // - The testlet finishes, in which case the channel "done" will be receive a value - // - The configured time limit is exceeded (expected for testlets running in server mode) - select { - case <-time.After(time.Duration(timeLimit) * time.Second): - log.Debug("Successfully killed ") - return map[string]string{}, nil - - case err := <-done: - if err != nil { - return map[string]string{}, errors.New("testlet error") - } else { - log.Debugf("Testlet completed without error") - return metrics, nil - } - } -} - -// Kill is currently unimplemented. This will have to be coordinated with "Run". Basically -// you need a way to kill this testlet (and that's really only possible when running -// async) Probably just want to set the channel to something so the select within "Run" will execute -func (b BaseTestlet) Kill() error { - return nil -} - -// IsNativeTestlet polls the list of registered native testlets, and returns -// true if the referenced name exists -func IsNativeTestlet(name string) (bool, string) { - if _, ok := nativeTestlets[name]; ok { - return true, nativeTestlets[name] - } else { - return false, "" - } -} - -//NewTestlet produces a new testlet based on the "name" param -func NewTestlet(name string) (Testlet, error) { - - if testlet, ok := testlets[name]; ok { - return testlet, nil - } else { - return nil, errors.New( - fmt.Sprintf("'%s' not currently supported as a native testlet"), - ) - } -} - -// Register makes a testlet available by the provided name. -// If Register is called twice with the same name or if testlet is nil, -// it will return an error -func Register(name string, testlet Testlet) error { - testletsMu.Lock() - defer testletsMu.Unlock() - if testlet == nil { - return errors.New("Register testlet is nil") - } - if _, dup := testlets[name]; dup { - return errors.New("Register called twice for testlet " + name) - } - testlets[name] = testlet - return nil -} - -func unregisterAllTestlets() { - testletsMu.Lock() - defer testletsMu.Unlock() - // For tests. - testlets = make(map[string]Testlet) -} - -// Testlets returns a sorted list of the names of the registered testlets. -func Testlets() []string { - testletsMu.RLock() - defer testletsMu.RUnlock() - var list []string - for name := range testlets { - list = append(list, name) - } - sort.Strings(list) - return list -} From 84ea3b72786a7438f049ab4c205d0881ea036c34 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Mon, 19 Sep 2016 23:03:05 -0700 Subject: [PATCH 084/120] Reorganized testing logic Signed-off-by: Matt Oswalt --- agent/tasks/executetestrun.go | 4 +- agent/tasks/installtestrun.go | 4 +- agent/testing/testing.go | 118 ++++++++++++++++++---------------- cmd/todd-agent/main.go | 54 ++++++++++++++-- 4 files changed, 116 insertions(+), 64 deletions(-) diff --git a/agent/tasks/executetestrun.go b/agent/tasks/executetestrun.go index 90776bb..d3cf2be 100644 --- a/agent/tasks/executetestrun.go +++ b/agent/tasks/executetestrun.go @@ -21,7 +21,7 @@ import ( log "github.com/Sirupsen/logrus" "github.com/Mierdin/todd/agent/cache" - "github.com/Mierdin/todd/agent/testing/testlets" + "github.com/Mierdin/todd/agent/testing" "github.com/Mierdin/todd/config" ) @@ -66,7 +66,7 @@ func (ett ExecuteTestRunTask) Run() error { wg.Add(len(tr.Targets)) var testlet_path string - isNative, newTestletName := testlets.IsNativeTestlet(tr.Testlet) + isNative, newTestletName := testing.IsNativeTestlet(tr.Testlet) // If we're running a native testlet, we want testlet_path to simply be the testlet name // (since it is a requirement that the native-Go testlets are in the PATH) diff --git a/agent/tasks/installtestrun.go b/agent/tasks/installtestrun.go index 355e45e..ccfd711 100644 --- a/agent/tasks/installtestrun.go +++ b/agent/tasks/installtestrun.go @@ -20,7 +20,7 @@ import ( "github.com/Mierdin/todd/agent/cache" "github.com/Mierdin/todd/agent/defs" - "github.com/Mierdin/todd/agent/testing/testlets" + "github.com/Mierdin/todd/agent/testing" "github.com/Mierdin/todd/config" ) @@ -46,7 +46,7 @@ func (itt InstallTestRunTask) Run() error { var testlet_path string // Determine if this is a native testlet - isNative, newName := testlets.IsNativeTestlet(itt.Tr.Testlet) + isNative, newName := testing.IsNativeTestlet(itt.Tr.Testlet) // If we're running a native testlet, we want testlet_path to simply be the testlet name // (since it is a requirement that the native-Go testlets are in the PATH) diff --git a/agent/testing/testing.go b/agent/testing/testing.go index 58489ed..9155e9d 100644 --- a/agent/testing/testing.go +++ b/agent/testing/testing.go @@ -1,68 +1,78 @@ /* - ToDD Agent - Test Reporting + ToDD testing package - This file contains functions that watch the agent cache and upload test data when present. + Contains infrastructure running testlets as well as maintaining + conformance for other native-Go testlet projects - Copyright 2016 Matt Oswalt. Use or modification of this - source code is governed by the license provided here: - https://github.com/Mierdin/todd/blob/master/LICENSE + Copyright 2016 Matt Oswalt. Use or modification of this + source code is governed by the license provided here: + https://github.com/Mierdin/todd/blob/master/LICENSE */ package testing -import ( - "errors" - "time" +var ( - log "github.com/Sirupsen/logrus" - - "github.com/Mierdin/todd/agent/cache" - "github.com/Mierdin/todd/agent/responses" - "github.com/Mierdin/todd/comms" - "github.com/Mierdin/todd/config" + // This map provides name redirection so that the native testlets can use names that don't + // conflict with existing system tools (i.e. using "toddping" instead of "ping") but users + // can still refer to the testlets using simple names. + // + // In short, users refer to the testlet by and this map will redirect to the + // actual binary name + nativeTestlets = map[string]string{ + "ping": "toddping", + } ) -// WatchForFinishedTestRuns simply watches the local cache for any test runs that have test data. -// It will periodically look at the table and send any present test data back to the server as a response. -// When the server has successfully received this data, it will send a task back to this specific agent -// to delete this row from the cache. -func WatchForFinishedTestRuns(cfg config.Config) error { - - var ac = cache.NewAgentCache(cfg) - - agentUuid := ac.GetKeyValue("uuid") - - for { - - time.Sleep(5000 * time.Millisecond) - - testruns, err := ac.GetFinishedTestRuns() - if err != nil { - log.Error("Problem retrieving finished test runs") - return errors.New("Problem retrieving finished test runs") - } - - for testUuid, testData := range testruns { - - log.Debug("Found ripe testrun: ", testUuid) - - var utdr = responses.UploadTestDataResponse{ - TestUuid: testUuid, - TestData: testData, - } - utdr.AgentUuid = agentUuid - utdr.Type = "TestData" //TODO(mierdin): This is an extra step. Maybe a factory function for the task could help here? - - tc, err := comms.NewToDDComms(cfg) - if err != nil { - return err - } - tc.CommsPackage.SendResponse(utdr) +// Testlet defines what a testlet should look like if built in native +// go and compiled with the agent +type Testlet interface { + + // Run is the "workflow" function for a testlet. All testing takes place here + // (or in a function called within) + // + // Params are + // target (string) + // args ([]string) + // timeLimit (int in seconds) + // + // Returns: + // metrics (map[string]string) + // (name of metric is key, value is metric value) + Run(string, []string, int) (map[string]string, error) +} - } +// NOTE +// +// Early efforts to build native-Go testlets involved the embedding of testlet logic into the +// ToDD agent itself. As a result, it was important to build some reusable infrastructure so that goroutines +// running testlet code within the agent could be controlled, and that new testlets could benefit from this +// infrastructure. +// +// Since then, the decision was made to keep testlets as their own separate binaries. +// +// These testlets are in their own repositories, and they do actually use some of the logic below, just not as meaningfully +// and comprehensively as they would have if they were baked in to the agent. The development standard for all "blessed" +// testlets will still ensure that they use this interface, so that if we decide to bake them into the agent in the future, +// they'll already conform. +// +// (The vast majority of this code was inspired by the database drivers implementation in the stdlib) + +type rtfunc func(target string, args []string, timeout int) (map[string]string, error) + +type BaseTestlet struct { + + // rtfunc is a type that will store our RunTestlet function. It is the responsibility + // of the "child" testlet to set this value upon creation + RunFunction rtfunc +} +// IsNativeTestlet polls the list of registered native testlets, and returns +// true if the referenced name exists +func IsNativeTestlet(name string) (bool, string) { + if _, ok := nativeTestlets[name]; ok { + return true, nativeTestlets[name] + } else { + return false, "" } - - return nil - } diff --git a/cmd/todd-agent/main.go b/cmd/todd-agent/main.go index a7f527e..aa39c81 100644 --- a/cmd/todd-agent/main.go +++ b/cmd/todd-agent/main.go @@ -9,6 +9,7 @@ package main import ( + "errors" "flag" "fmt" "os" @@ -19,14 +20,10 @@ import ( "github.com/Mierdin/todd/agent/cache" "github.com/Mierdin/todd/agent/defs" "github.com/Mierdin/todd/agent/facts" - "github.com/Mierdin/todd/agent/testing" + "github.com/Mierdin/todd/agent/responses" "github.com/Mierdin/todd/comms" "github.com/Mierdin/todd/config" "github.com/Mierdin/todd/hostresources" - // Testlet imports (importing these packages registers the testlets) - // Need to make this dynamic if possible (at compile time of course) - // TODO Not necessary anymore - //_ "github.com/toddproject/todd-nativetestlet-ping/testlet" ) // Command-line Arguments @@ -70,7 +67,7 @@ func main() { log.Infof("ToDD Agent Activated: %s", uuid) // Start test data reporting service - go testing.WatchForFinishedTestRuns(cfg) + go watchForFinishedTestRuns(cfg) // Construct comms package tc, err := comms.NewToDDComms(cfg) @@ -124,3 +121,48 @@ func main() { } } + +// watchForFinishedTestRuns simply watches the local cache for any test runs that have test data. +// It will periodically look at the table and send any present test data back to the server as a response. +// When the server has successfully received this data, it will send a task back to this specific agent +// to delete this row from the cache. +func watchForFinishedTestRuns(cfg config.Config) error { + + var ac = cache.NewAgentCache(cfg) + + agentUuid := ac.GetKeyValue("uuid") + + for { + + time.Sleep(5000 * time.Millisecond) + + testruns, err := ac.GetFinishedTestRuns() + if err != nil { + log.Error("Problem retrieving finished test runs") + return errors.New("Problem retrieving finished test runs") + } + + for testUuid, testData := range testruns { + + log.Debug("Found ripe testrun: ", testUuid) + + var utdr = responses.UploadTestDataResponse{ + TestUuid: testUuid, + TestData: testData, + } + utdr.AgentUuid = agentUuid + utdr.Type = "TestData" //TODO(mierdin): This is an extra step. Maybe a factory function for the task could help here? + + tc, err := comms.NewToDDComms(cfg) + if err != nil { + return err + } + tc.CommsPackage.SendResponse(utdr) + + } + + } + + return nil + +} From b5aeda3ed9fd6df293d787fd9909f55e3109097d Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Tue, 20 Sep 2016 14:51:01 -0700 Subject: [PATCH 085/120] updated comment regarding future development script Signed-off-by: Matt Oswalt --- Makefile | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index ac80285..3352712 100644 --- a/Makefile +++ b/Makefile @@ -11,11 +11,13 @@ build: docker build -t mierdin/todd -f Dockerfile . compile: - # TODO(mierdin): Need to support some kind of mode that allows for development. - # The current gettestlets.sh script downloads the testlets from Github, meaning - # a developer would already have to have changes pushed to those repos' master - # Looking for something like devstack, etc. + # TODO(mierdin): The current gettestlets.sh script downloads the testlets from + # Github, meaning a developer would already have to have changes pushed to + # those repos' master. # + # In a follow-up patch, a script will be provided that allows for rapid development + # (uses local repositories instead of pulling from GH). Something like devstack. + # Installing testlets ./scripts/gettestlets.sh From 37fc165e89ab1e0e90b22c766d1cd67598c20125 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Tue, 20 Sep 2016 15:06:44 -0700 Subject: [PATCH 086/120] removed sudo from sysctl command in makefile Signed-off-by: Matt Oswalt --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 3352712..c021bdc 100644 --- a/Makefile +++ b/Makefile @@ -54,4 +54,4 @@ configureenv: chmod -R 777 /opt/todd # If on Linux, enable ping testlet functionality - sudo sysctl -w net.ipv4.ping_group_range="0 12345" + sysctl -w net.ipv4.ping_group_range="0 12345" From e53e916fe8241c75f4e39e189e64d953a64a7c29 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Tue, 20 Sep 2016 17:05:26 -0700 Subject: [PATCH 087/120] Updates to makefile and vagrantfile Signed-off-by: Matt Oswalt --- Makefile | 2 +- Vagrantfile | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c021bdc..7f08d9c 100644 --- a/Makefile +++ b/Makefile @@ -54,4 +54,4 @@ configureenv: chmod -R 777 /opt/todd # If on Linux, enable ping testlet functionality - sysctl -w net.ipv4.ping_group_range="0 12345" + sysctl -w net.ipv4.ping_group_range="0 0" || echo "Unable to set kernel parameters to allow ping. Some testlets may not work." diff --git a/Vagrantfile b/Vagrantfile index 9317dee..dc26d0e 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -7,6 +7,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.box = "trusty64" config.vm.box_url = "http://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-amd64-vagrant-disk1.box" config.vm.synced_folder '.', '/home/vagrant/go/src/github.com/Mierdin/todd', nfs: true + config.vm.synced_folder '../../toddproject/todd-nativetestlet-ping', '/home/vagrant/go/src/github.com/toddproject/todd-nativetestlet-ping', nfs: true config.vm.provider "virtualbox" do |v| From 8ddffcfde0883cf6e1ea3ddd713c449b47eff7e2 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Thu, 22 Sep 2016 14:10:43 -0700 Subject: [PATCH 088/120] Added script to add socket capabilities to testlet binaries on install Signed-off-by: Matt Oswalt --- Makefile | 8 +++++--- scripts/set-testlet-capabilities.sh | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 scripts/set-testlet-capabilities.sh diff --git a/Makefile b/Makefile index 7f08d9c..3338257 100644 --- a/Makefile +++ b/Makefile @@ -24,8 +24,6 @@ compile: # Installing ToDD go install ./cmd/... -install: configureenv - fmt: go fmt github.com/mierdin/todd/... @@ -47,7 +45,11 @@ start: compile # that's why "server-int.cfg" and "agent-int.cfg" are being used here. start-containers.sh 3 /etc/todd/server-int.cfg /etc/todd/agent-int.cfg -configureenv: +install: + + # Set testlet capabilities + ./scripts/set-testlet-capabilities.sh + # Copy configs if etc and /etc/todd aren't linked if ! [ "etc" -ef "/etc/todd" ]; then mkdir -p /etc/todd && cp -f ./etc/{agent,server}.cfg /etc/todd/; fi mkdir -p /opt/todd/{agent,server}/assets/{factcollectors,testlets} diff --git a/scripts/set-testlet-capabilities.sh b/scripts/set-testlet-capabilities.sh new file mode 100644 index 0000000..f37a749 --- /dev/null +++ b/scripts/set-testlet-capabilities.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +# Copyright 2016 Matt Oswalt. Use or modification of this +# source code is governed by the license provided here: +# https://github.com/mierdin/todd/blob/master/LICENSE + +# This script configures appropriate capabilities for testlet binaries + +set -e +set -u +set -o pipefail + +# Ensure GOPATH is set +: ${GOPATH:?"Please ensure GOPATH is set, and run sudo with -E when performing 'make install'"} + +# Enable raw socket capabilities on toddping +setcap cap_net_raw+ep $GOPATH/bin/toddping \ No newline at end of file From 376693ea783596c3ca38897b15f9a20f99117919 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Thu, 22 Sep 2016 14:12:59 -0700 Subject: [PATCH 089/120] Made capabilities script exec Signed-off-by: Matt Oswalt --- scripts/set-testlet-capabilities.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 scripts/set-testlet-capabilities.sh diff --git a/scripts/set-testlet-capabilities.sh b/scripts/set-testlet-capabilities.sh old mode 100644 new mode 100755 From ecf6784778b6ac07e7132dcea02e29be4b1b9323 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Thu, 22 Sep 2016 15:09:12 -0700 Subject: [PATCH 090/120] Added some comments about install process Signed-off-by: Matt Oswalt --- Makefile | 10 ++-------- scripts/gettestlets.sh | 2 ++ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 3338257..0b9318c 100644 --- a/Makefile +++ b/Makefile @@ -11,12 +11,6 @@ build: docker build -t mierdin/todd -f Dockerfile . compile: - # TODO(mierdin): The current gettestlets.sh script downloads the testlets from - # Github, meaning a developer would already have to have changes pushed to - # those repos' master. - # - # In a follow-up patch, a script will be provided that allows for rapid development - # (uses local repositories instead of pulling from GH). Something like devstack. # Installing testlets ./scripts/gettestlets.sh @@ -55,5 +49,5 @@ install: mkdir -p /opt/todd/{agent,server}/assets/{factcollectors,testlets} chmod -R 777 /opt/todd - # If on Linux, enable ping testlet functionality - sysctl -w net.ipv4.ping_group_range="0 0" || echo "Unable to set kernel parameters to allow ping. Some testlets may not work." + # If on Linux, enable ping testlet functionality (DEPRECATED in favor of granting socket capabilities on testlets) + # sysctl -w net.ipv4.ping_group_range="0 0" || echo "Unable to set kernel parameters to allow ping. Some testlets may not work." diff --git a/scripts/gettestlets.sh b/scripts/gettestlets.sh index c8e56ac..e34a431 100755 --- a/scripts/gettestlets.sh +++ b/scripts/gettestlets.sh @@ -19,5 +19,7 @@ testlets=( for i in "${testlets[@]}" do echo "Installing $i" + + # This retrieves from GH if it doesn't exist, but if it does, it installs the local copy. go get -u $i/cmd/... done From 213cc9767da616c3e7930854483a31fdaa3d7683 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Tue, 19 Jul 2016 01:35:36 -0700 Subject: [PATCH 091/120] Saving progress on building native testlet infrastructure Signed-off-by: Matt Oswalt --- agent/tasks/executetestrun.go | 42 +++++---------- agent/tasks/installtestrun.go | 57 +++++++++++---------- agent/testing/testlets/_ping | 32 ++++++++++++ agent/testing/testlets/ping/ping.go | 46 +++++++++++++++++ agent/testing/testlets/testlets.go | 79 +++++++++++++++++++++++++++++ 5 files changed, 199 insertions(+), 57 deletions(-) create mode 100755 agent/testing/testlets/_ping create mode 100644 agent/testing/testlets/ping/ping.go create mode 100644 agent/testing/testlets/testlets.go diff --git a/agent/tasks/executetestrun.go b/agent/tasks/executetestrun.go index d3cf2be..06c71cf 100644 --- a/agent/tasks/executetestrun.go +++ b/agent/tasks/executetestrun.go @@ -12,7 +12,6 @@ import ( "bytes" "encoding/json" "errors" - "fmt" "os" "os/exec" "sync" @@ -47,9 +46,10 @@ func (ett ExecuteTestRunTask) Run() error { var wg sync.WaitGroup // Waiting three seconds to ensure all the agents have their tasks before we potentially hammer the network + // // TODO(mierdin): This is a temporary measure - in the future, testruns will be executed via time schedule, // making not only this sleep, but also the entire task unnecessary. Testruns will simply be installed, and - // executed when the time is right. + // executed when the time is right. This is, in part tracked by https://github.com/Mierdin/todd/issues/89 time.Sleep(3000 * time.Millisecond) // Retrieve test from cache by UUID @@ -65,26 +65,11 @@ func (ett ExecuteTestRunTask) Run() error { // Specify size of wait group equal to number of targets wg.Add(len(tr.Targets)) - var testlet_path string - isNative, newTestletName := testing.IsNativeTestlet(tr.Testlet) - - // If we're running a native testlet, we want testlet_path to simply be the testlet name - // (since it is a requirement that the native-Go testlets are in the PATH) - // If the testlet is not native, we can get the full path. - if isNative { - testlet_path = newTestletName - } else { - // Generate path to testlet and make sure it exists. - testlet_path = fmt.Sprintf("%s/assets/testlets/%s", ett.Config.LocalResources.OptDir, tr.Testlet) - if _, err := os.Stat(testlet_path); os.IsNotExist(err) { - log.Errorf("Testlet %s does not exist on this agent", testlet_path) - return errors.New("Error executing testrun - testlet doesn't exist on this agent.") - } + testletPath, err := testing.GetTestletPath(tr.Testlet, ett.Config.LocalResources.OptDir) + if err != nil { + return err } - // TODO(mierdin): What about testlets running as servers (i.e. 'iperf -s')? Are we spinning up len(tr.Targets) - // number of those? - // Execute testlets against all targets asynchronously for i := range tr.Targets { @@ -94,19 +79,18 @@ func (ett ExecuteTestRunTask) Run() error { defer wg.Done() - log.Debugf("Full testlet command and args: '%s %s %s'", testlet_path, thisTarget, tr.Args) - cmd := exec.Command(testlet_path, thisTarget, tr.Args) + log.Debugf("Full testlet command and args: '%s %s %s'", testletPath, thisTarget, tr.Args) + cmd := exec.Command(testletPath, thisTarget, tr.Args) // Stdout buffer cmdOutput := &bytes.Buffer{} // Attach buffer to command cmd.Stdout = cmdOutput - // Execute collector + // Execute testlet cmd.Start() - // TODO(mierdin): Why is this a buffered channel? Is this necessary? - done := make(chan error, 1) + done := make(chan error) go func() { done <- cmd.Wait() }() @@ -117,16 +101,16 @@ func (ett ExecuteTestRunTask) Run() error { select { case <-time.After(time.Duration(ett.TimeLimit) * time.Second): if err := cmd.Process.Kill(); err != nil { - log.Errorf("Failed to kill %s after timeout: %s", testlet_path, err) + log.Errorf("Failed to kill %s after timeout: %s", testletPath, err) } else { - log.Debug("Successfully killed ", testlet_path) + log.Debug("Successfully killed ", testletPath) } case err := <-done: if err != nil { - log.Errorf("Testlet %s completed with error '%s'", testlet_path, err) + log.Errorf("Testlet %s completed with error '%s'", testletPath, err) gatheredData[thisTarget] = "error" } else { - log.Debugf("Testlet %s completed without error", testlet_path) + log.Debugf("Testlet %s completed without error", testletPath) } } diff --git a/agent/tasks/installtestrun.go b/agent/tasks/installtestrun.go index ccfd711..08594e4 100644 --- a/agent/tasks/installtestrun.go +++ b/agent/tasks/installtestrun.go @@ -43,48 +43,49 @@ func (itt InstallTestRunTask) Run() error { return errors.New("Testlet parameter for this testrun is null") } - var testlet_path string + // Determine if this is a valid native testlet + _, err := testlets.NewTestlet(itt.Tr.Testlet) - // Determine if this is a native testlet - isNative, newName := testing.IsNativeTestlet(itt.Tr.Testlet) + // Not a native testlet - attempt to run check mode on testlet in filesystem + if err != nil { - // If we're running a native testlet, we want testlet_path to simply be the testlet name - // (since it is a requirement that the native-Go testlets are in the PATH) - // If the testlet is not native, we can get the full path. - if isNative { - log.Infof("%s is a native testlet - installing testrun.", itt.Tr.Testlet) - testlet_path = newName - } else { // Generate path to testlet and make sure it exists. - testlet_path = fmt.Sprintf("%s/assets/testlets/%s", itt.Config.LocalResources.OptDir, itt.Tr.Testlet) + testlet_path := fmt.Sprintf("%s/assets/testlets/%s", itt.Config.LocalResources.OptDir, itt.Tr.Testlet) + if _, err := os.Stat(testlet_path); os.IsNotExist(err) { log.Errorf("Testlet %s does not exist on this agent", itt.Tr.Testlet) return errors.New("Error installing testrun - testlet doesn't exist on this agent.") } - } - // Run the testlet in check mode to verify that everything is okay to run this test - log.Debug("Running testlet in check mode: ", testlet_path) - cmd := exec.Command(testlet_path, "check") - - // Stdout buffer - cmdOutput := &bytes.Buffer{} - // Attach buffer to command - cmd.Stdout = cmdOutput - // Execute collector - cmd.Run() + // Run the testlet in check mode to verify that everything is okay to run this test + log.Debug("Running testlet in check mode: ", testlet_path) + cmd := exec.Command(testlet_path, "check") + + // Stdout buffer + cmdOutput := &bytes.Buffer{} + // Attach buffer to command + cmd.Stdout = cmdOutput + // Execute collector + cmd.Run() + + // This is probably the best cross-platform way to see if check mode passed. + if strings.Contains(string(cmdOutput.Bytes()), "Check mode PASSED") { + log.Debugf("Check mode for %s passed", testlet_path) + } else { + log.Error("Testlet returned an error during check mode: ", string(cmdOutput.Bytes())) + return errors.New("Testlet returned an error during check mode") + } - // This is probably the best cross-platform way to see if check mode passed. - if strings.Contains(string(cmdOutput.Bytes()), "Check mode PASSED") { - log.Debugf("Check mode for %s passed", testlet_path) } else { - log.Error("Testlet returned an error during check mode: ", string(cmdOutput.Bytes())) - return errors.New("Testlet returned an error during check mode") + + // Nothing to do, as we're using a native testlet + log.Infof("%s is a native testlet - installing testrun.", itt.Tr.Testlet) + } // Insert testrun into agent cache var ac = cache.NewAgentCache(itt.Config) - err := ac.InsertTestRun(itt.Tr) + err = ac.InsertTestRun(itt.Tr) if err != nil { log.Error(err) return errors.New("Problem installing test run into agent cache") diff --git a/agent/testing/testlets/_ping b/agent/testing/testlets/_ping new file mode 100755 index 0000000..aed17d4 --- /dev/null +++ b/agent/testing/testlets/_ping @@ -0,0 +1,32 @@ +#!/usr/bin/env bash + +# Copyright 2016 Matt Oswalt. Use or modification of this +# source code is governed by the license provided here: +# https://github.com/Mierdin/todd/blob/master/LICENSE + +check() { + echo "Running in check mode" + command -v ping >/dev/null 2>&1 || { echo >&2 "Ping is not installed. Exiting."; exit 1; } + echo "Check mode PASSED" + exit 0 +} + +if [[ $1 == "check" ]]; + then + check +fi + +# The first argument after the testlet name will ALWAYS be the target. So, $1 is always the target, and all arguments from $2 and up should be passed into the app command +app_args="$@" +app_args=${app_args#$0 } +app_args=${app_args#$1 } + +app_run_command="ping $app_args $1" + +output=$($app_run_command) + +packet_loss=$(echo $output | tr '\n' ' ' | sed -n 's/.* \(.*\)% packet loss.*/\1/p') +avg_latency=$(echo $output | tr '\n' ' ' | sed -n 's/.*dev = .*\/\(.*\)\/.*\/.*/\1/p') + +teslet_data='{"packet_loss_percentage":"'$packet_loss'", "avg_latency_ms":"'$avg_latency'"}' +echo $teslet_data \ No newline at end of file diff --git a/agent/testing/testlets/ping/ping.go b/agent/testing/testlets/ping/ping.go new file mode 100644 index 0000000..f68b43b --- /dev/null +++ b/agent/testing/testlets/ping/ping.go @@ -0,0 +1,46 @@ +package toddping + +// NOTE ////////////////// +// +// This is a built-in testlet. Currently, the approach is to have each +// testlet under it's own package, which is explicitly imported under +// the ToDD agent's 'main' package. +// +// Currently, each testlet is stored within the ToDD repo in order to +// vet out the architecture, which means they are awkwardly placed +// underneath the "testlets" directory together. However, this should be +// a tempoarary holding place, as the main effort around native testlets +// is being implemented so they can be broken out into their own repos. + +import ( + "github.com/Mierdin/todd/agent/testing/testlets" +) + +type PingTestlet struct{} + +func init() { + testlets.Register("ping", &PingTestlet{}) +} + +// TODO(mierdin): Maybe consider running these asyc by default? Basically +// the "Run" function kicks back a channel of type map[string]string so when +// it's populated, it contains the metrics and you know it can stop + +func (p PingTestlet) Run(target string, args []string) (map[string]string, error) { + + // TODO(mierdin): Implement ping test here + + return map[string]string{}, nil +} + +func (p PingTestlet) Kill() error { + // TODO (mierdin): This will have to be coordinated with the task above. Basically + // you need a way to kill this testlet (and that's really only possible when running + // async) + + return nil +} + +func (p PingTestlet) Test() string { + return "trolololol" +} diff --git a/agent/testing/testlets/testlets.go b/agent/testing/testlets/testlets.go new file mode 100644 index 0000000..6ad65cf --- /dev/null +++ b/agent/testing/testlets/testlets.go @@ -0,0 +1,79 @@ +package testlets + +import ( + "errors" + "fmt" + "sort" + "sync" + //"sync/atomic" +) + +var ( + testletsMu sync.RWMutex + testlets = make(map[string]Testlet) +) + +// Testlet defines what a testlet should look like if built in native +// go and compiled with the agent +type Testlet interface { + + // Params are + // target (string) + // args ([]string) + // + // Returns: + // metrics (map[string]interface{}) + // (name of metric is key, value is metric value) + Run(string, []string) (map[string]string, error) + + Kill() error + + Test() string //TODO(mierdin): Remove me +} + +//NewTestlet produces a new testlet based on the "name" param +func NewTestlet(name string) (Testlet, error) { + + if testlet, ok := testlets[name]; ok { + return testlet, nil + } else { + return nil, errors.New( + fmt.Sprintf("'%s' not currently supported as a native testlet"), + ) + } +} + +// Register makes a testlet available by the provided name. +// If Register is called twice with the same name or if testlet is nil, +// it will return an error +func Register(name string, testlet Testlet) error { + testletsMu.Lock() + defer testletsMu.Unlock() + if testlet == nil { + return errors.New("Register testlet is nil") + } + if _, dup := testlets[name]; dup { + return errors.New("Register called twice for testlet " + name) + } + testlets[name] = testlet + return nil +} + +func unregisterAllDrivers() { + testletsMu.Lock() + defer testletsMu.Unlock() + // For tests. + testlets = make(map[string]Testlet) +} + +// Testlets returns a sorted list of the names of the registered testlets. +func Testlets() []string { + testletsMu.RLock() + defer testletsMu.RUnlock() + var list []string + for name := range testlets { + list = append(list, name) + } + sort.Strings(list) + return list +} From 0b858736cd150f467fc1a85735f9a026bec39854 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Tue, 19 Jul 2016 01:36:05 -0700 Subject: [PATCH 092/120] Docs update Signed-off-by: Matt Oswalt --- docs/nativetests.rst | 6 ++++++ docs/testlets.rst | 14 ++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 docs/nativetests.rst diff --git a/docs/nativetests.rst b/docs/nativetests.rst new file mode 100644 index 0000000..e6c04a3 --- /dev/null +++ b/docs/nativetests.rst @@ -0,0 +1,6 @@ +Native Tests +================================ + +Need to talk about the native tests you've built in, and turn the "testlets" doc into more of a "so you want to build your own, eh?" + +Also need to figure out if you want to refer to both native and non-native as "testlets", or maybe reserve that for non-native \ No newline at end of file diff --git a/docs/testlets.rst b/docs/testlets.rst index 092742d..ab6ba51 100644 --- a/docs/testlets.rst +++ b/docs/testlets.rst @@ -12,6 +12,20 @@ There are a number of testlets built-in to the ToDD agent and are usable simply These have their own separate repositories and are distributed alongside ToDD proper. They are written in Go for a number of reasons. First, it makes it easy for the testlets to honor the testlet format by leveraging some common code in the ToDD repository. However, the testlets are still their own binary. In addition, it allows ToDD to execute tests consistently across platforms (The old model of using bash scripts meant the tests had to be run on a certain platform for which that testlet knew how to parse the output) +If you don't want to use any of the built-in testlets, you can, of course, build your own testlet (provided it follows the standard defined on this page) and refer to it by it's filename. + +Check Mode +---------- +Each testlet must support a "check mode". This is a way of running a testlet that allows the ToDD agent to know whether or not a test can be performed, without actually running the test. + +For instance, when the ToDD agent runs the "ping" testlet in check mode, it would invoke it like this: + +.. code-block:: text + + ./testletname check + +That said, the ToDD Server will distribute testrun instructions to the agents in two phases: + However, please see "Custom Testlets", and you'll find it's quite easy to build your own testlets and run them with ToDD. This extensibility was a core design principle of ToDD since the beginning of the project. From c781b56ff958c847b138034964b7c993b5cb844d Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Thu, 28 Jul 2016 01:45:12 -0700 Subject: [PATCH 093/120] Made progress on a native testlet implementation Signed-off-by: Matt Oswalt --- agent/testing/testlets/ping/ping.go | 21 +++++++++++---------- agent/testing/testlets/testlets.go | 10 +++++++++- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/agent/testing/testlets/ping/ping.go b/agent/testing/testlets/ping/ping.go index f68b43b..51f29e6 100644 --- a/agent/testing/testlets/ping/ping.go +++ b/agent/testing/testlets/ping/ping.go @@ -13,24 +13,29 @@ package toddping // is being implemented so they can be broken out into their own repos. import ( + "time" + "github.com/Mierdin/todd/agent/testing/testlets" ) type PingTestlet struct{} func init() { + + // This is important - register the name of this testlet + // (the name the user will use in a testrun definition) testlets.Register("ping", &PingTestlet{}) } -// TODO(mierdin): Maybe consider running these asyc by default? Basically -// the "Run" function kicks back a channel of type map[string]string so when -// it's populated, it contains the metrics and you know it can stop - func (p PingTestlet) Run(target string, args []string) (map[string]string, error) { - // TODO(mierdin): Implement ping test here + // TODO(mierdin): Implement ping test here - this is just a mock + time.Sleep(3000 * time.Millisecond) + return map[string]string{ + "avg_latency_ms": "25.144", + "packet_loss_percentage": "0", + }, nil - return map[string]string{}, nil } func (p PingTestlet) Kill() error { @@ -40,7 +45,3 @@ func (p PingTestlet) Kill() error { return nil } - -func (p PingTestlet) Test() string { - return "trolololol" -} diff --git a/agent/testing/testlets/testlets.go b/agent/testing/testlets/testlets.go index 6ad65cf..64ed586 100644 --- a/agent/testing/testlets/testlets.go +++ b/agent/testing/testlets/testlets.go @@ -27,8 +27,16 @@ type Testlet interface { Run(string, []string) (map[string]string, error) Kill() error +} - Test() string //TODO(mierdin): Remove me +// IsNativeTestlet polls the list of registered native testlets, and returns +// true if the referenced name exists +func IsNativeTestlet(name string) bool { + if _, ok := testlets[name]; ok { + return true + } else { + return false + } } //NewTestlet produces a new testlet based on the "name" param From 60925b16be580bb53e84e9e503da6f6e89759e06 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Fri, 29 Jul 2016 02:21:01 -0700 Subject: [PATCH 094/120] Implement async handling of testlet running Signed-off-by: Matt Oswalt --- agent/testing/testlets/ping/ping.go | 29 ++++++----- agent/testing/testlets/testlets.go | 81 ++++++++++++++++++++++++++++- 2 files changed, 96 insertions(+), 14 deletions(-) diff --git a/agent/testing/testlets/ping/ping.go b/agent/testing/testlets/ping/ping.go index 51f29e6..8ceb887 100644 --- a/agent/testing/testlets/ping/ping.go +++ b/agent/testing/testlets/ping/ping.go @@ -9,25 +9,38 @@ package toddping // Currently, each testlet is stored within the ToDD repo in order to // vet out the architecture, which means they are awkwardly placed // underneath the "testlets" directory together. However, this should be -// a tempoarary holding place, as the main effort around native testlets +// a temporary holding place, as the main effort around native testlets // is being implemented so they can be broken out into their own repos. import ( "time" + // log "github.com/Sirupsen/logrus" + "github.com/Mierdin/todd/agent/testing/testlets" ) -type PingTestlet struct{} +type PingTestlet struct { + testlets.BaseTestlet +} func init() { + var pt = PingTestlet{} + + // Ensure the RunFunction attribute is set correctly. + // This allows the underlying testlet infrastructure + // to know what function to call at runtime + pt.RunFunction = pt.RunTestlet + // This is important - register the name of this testlet // (the name the user will use in a testrun definition) - testlets.Register("ping", &PingTestlet{}) + testlets.Register("ping", &pt) } -func (p PingTestlet) Run(target string, args []string) (map[string]string, error) { +// RunTestlet implements the core logic of the testlet. Don't worry about running asynchronously, +// that's handled by the infrastructure. +func (p PingTestlet) RunTestlet(target string, args []string) (map[string]string, error) { // TODO(mierdin): Implement ping test here - this is just a mock time.Sleep(3000 * time.Millisecond) @@ -37,11 +50,3 @@ func (p PingTestlet) Run(target string, args []string) (map[string]string, error }, nil } - -func (p PingTestlet) Kill() error { - // TODO (mierdin): This will have to be coordinated with the task above. Basically - // you need a way to kill this testlet (and that's really only possible when running - // async) - - return nil -} diff --git a/agent/testing/testlets/testlets.go b/agent/testing/testlets/testlets.go index 64ed586..0ebb0c7 100644 --- a/agent/testing/testlets/testlets.go +++ b/agent/testing/testlets/testlets.go @@ -5,30 +5,104 @@ import ( "fmt" "sort" "sync" + "time" //"sync/atomic" + + log "github.com/Sirupsen/logrus" ) var ( testletsMu sync.RWMutex testlets = make(map[string]Testlet) + done = make(chan error) ) // Testlet defines what a testlet should look like if built in native // go and compiled with the agent type Testlet interface { + // Run is the "workflow" function for a testlet. It handles running + // the RunTestlet function asynchronously and managing the state therein. + // // Params are // target (string) // args ([]string) + // timeLimit (int in seconds) // // Returns: // metrics (map[string]interface{}) // (name of metric is key, value is metric value) - Run(string, []string) (map[string]string, error) + // + // Keep as much logic out of here as possible. All native testlets + // must support a "Kill" method, so it's best to implement core testlet + // logic in a separate function so that the Run and Kill commands can manage + // execution of that logic in a goroutine + Run(string, []string, int) (map[string]string, error) + // RunTestlet is designed to be the one-stop shop for testlet logic. + // The developer of a native testlet just needs to implement the testlet logic here, + // without worrying about things like managing goroutines or channels. That's all + // managed by the "Run" or "Kill" functions + RunTestlet(string, []string) (map[string]string, error) + // TODO(mierdin): is this really the best name for it? Maybe something that's less confusing, less like "Run" + + // All testlets must be able to stop operation when sent a Kill command. Kill() error } +type rtfunc func(target string, args []string) (map[string]string, error) + +type BaseTestlet struct { + + // rtfunc is a type that will store our RunTestlet function. It is the responsibility + // of the "child" testlet to set this value upon creation + RunFunction rtfunc +} + +// Run takes care of running the testlet function and managing it's operation given the parameters provided +func (b BaseTestlet) Run(target string, args []string, timeLimit int) (map[string]string, error) { + + var metrics map[string]string + + // TODO(mierdin): ensure channel is nil + // done = make(chan error) + + // TODO(mierdin): Based on experimentation, this will keep running even if this function returns. + // Need to be sure about how this ends. Also might want to evaluate the same for the existing non-native model, likely has the same issue + go func() { + theseMetrics, err := b.RunFunction(target, args) + metrics = theseMetrics //TODO(mierdin): Gross. + done <- err + }() + + // This select statement will block until one of these two conditions are met: + // - The testlet finishes, in which case the channel "done" will be receive a value + // - The configured time limit is exceeded (expected for testlets running in server mode) + select { + case <-time.After(time.Duration(timeLimit) * time.Second): + log.Debug("Successfully killed ") + return map[string]string{}, nil + + case err := <-done: + if err != nil { + return map[string]string{}, errors.New("testlet error") // TODO(mierdin): elaborate? + } else { + log.Debugf("Testlet completed without error") + return metrics, nil + } + } +} + +func (b BaseTestlet) Kill() error { + // TODO (mierdin): This will have to be coordinated with the task above. Basically + // you need a way to kill this testlet (and that's really only possible when running + // async) + + // Probably just want to set the channel to something so the select within "Run" will execute + + return nil +} + // IsNativeTestlet polls the list of registered native testlets, and returns // true if the referenced name exists func IsNativeTestlet(name string) bool { @@ -43,6 +117,9 @@ func IsNativeTestlet(name string) bool { func NewTestlet(name string) (Testlet, error) { if testlet, ok := testlets[name]; ok { + + // testlet.runFunction = testlet.run + return testlet, nil } else { return nil, errors.New( @@ -67,7 +144,7 @@ func Register(name string, testlet Testlet) error { return nil } -func unregisterAllDrivers() { +func unregisterAllTestlets() { testletsMu.Lock() defer testletsMu.Unlock() // For tests. From a6b5136c2ef11b1d7191fa44116903b90ec46341 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Sun, 31 Jul 2016 16:30:10 -0700 Subject: [PATCH 095/120] Further progress Signed-off-by: Matt Oswalt --- agent/testing/testlets/ping/ping.go | 57 ++++++++++++++++++++++++++--- agent/testing/testlets/testlets.go | 10 +++-- 2 files changed, 57 insertions(+), 10 deletions(-) diff --git a/agent/testing/testlets/ping/ping.go b/agent/testing/testlets/ping/ping.go index 8ceb887..42cc8d3 100644 --- a/agent/testing/testlets/ping/ping.go +++ b/agent/testing/testlets/ping/ping.go @@ -13,9 +13,10 @@ package toddping // is being implemented so they can be broken out into their own repos. import ( + "fmt" "time" - // log "github.com/Sirupsen/logrus" + log "github.com/Sirupsen/logrus" "github.com/Mierdin/todd/agent/testing/testlets" ) @@ -40,13 +41,57 @@ func init() { // RunTestlet implements the core logic of the testlet. Don't worry about running asynchronously, // that's handled by the infrastructure. -func (p PingTestlet) RunTestlet(target string, args []string) (map[string]string, error) { +func (p PingTestlet) RunTestlet(target string, args []string, kill chan (bool)) (map[string]string, error) { + + // Get number of pings + count := 3 //TODO(mierdin): need to parse from 'args', or if omitted, use a default value + + log.Error(args) + + var latencies []float32 + var replies int + + // Execute ping once per count + i := 0 + for i < count { + select { + case <-kill: + // Terminating early; return empty metrics + return map[string]string{}, nil + default: + + //log.Debugf("Executing ping #%d", i) + + // Mocked ping logic + latency, replyReceived := pingTemp(count) + + latencies = append(latencies, latency) + + if replyReceived { + replies += 1 + } + + i += 1 + time.Sleep(1000 * time.Millisecond) + + } + } + + // Calculate metrics + var latencyTotal float32 = 0 + for _, value := range latencies { + latencyTotal += value + } + avg_latency_ms := latencyTotal / float32(len(latencies)) + packet_loss := (float32(count) - float32(replies)) / float32(count) - // TODO(mierdin): Implement ping test here - this is just a mock - time.Sleep(3000 * time.Millisecond) return map[string]string{ - "avg_latency_ms": "25.144", - "packet_loss_percentage": "0", + "avg_latency_ms": fmt.Sprintf("%.2f", avg_latency_ms), + "packet_loss": fmt.Sprintf("%.2f", packet_loss), }, nil } + +func pingTemp(count int) (float32, bool) { + return float32(count) * 4.234, true +} diff --git a/agent/testing/testlets/testlets.go b/agent/testing/testlets/testlets.go index 0ebb0c7..4458892 100644 --- a/agent/testing/testlets/testlets.go +++ b/agent/testing/testlets/testlets.go @@ -14,7 +14,8 @@ import ( var ( testletsMu sync.RWMutex testlets = make(map[string]Testlet) - done = make(chan error) + done = make(chan error) // Used from within the goroutine to inform the infrastructure it has finished + kill = make(chan bool) // Used from outside the goroutine to inform the goroutine to stop ) // Testlet defines what a testlet should look like if built in native @@ -43,14 +44,14 @@ type Testlet interface { // The developer of a native testlet just needs to implement the testlet logic here, // without worrying about things like managing goroutines or channels. That's all // managed by the "Run" or "Kill" functions - RunTestlet(string, []string) (map[string]string, error) + RunTestlet(string, []string, chan bool) (map[string]string, error) // TODO(mierdin): is this really the best name for it? Maybe something that's less confusing, less like "Run" // All testlets must be able to stop operation when sent a Kill command. Kill() error } -type rtfunc func(target string, args []string) (map[string]string, error) +type rtfunc func(target string, args []string, kill chan bool) (map[string]string, error) type BaseTestlet struct { @@ -66,11 +67,12 @@ func (b BaseTestlet) Run(target string, args []string, timeLimit int) (map[strin // TODO(mierdin): ensure channel is nil // done = make(chan error) + // kill = make(chan bool) // TODO(mierdin): Based on experimentation, this will keep running even if this function returns. // Need to be sure about how this ends. Also might want to evaluate the same for the existing non-native model, likely has the same issue go func() { - theseMetrics, err := b.RunFunction(target, args) + theseMetrics, err := b.RunFunction(target, args, kill) metrics = theseMetrics //TODO(mierdin): Gross. done <- err }() From 94fdc30dddc0f6844c8fb51d53bdbe543236aaa9 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Mon, 1 Aug 2016 18:50:16 -0700 Subject: [PATCH 096/120] Catch-up commit Signed-off-by: Matt Oswalt --- .gitignore | 1 + Makefile | 3 + .../_ping => bashtestlets/notatallping} | 0 agent/testing/testlets/ping/ping.go | 97 ------------------- docs/nativetests.rst | 16 ++- scripts/buildtestlets.sh | 69 +++++++++++++ 6 files changed, 88 insertions(+), 98 deletions(-) rename agent/testing/{testlets/_ping => bashtestlets/notatallping} (100%) delete mode 100644 agent/testing/testlets/ping/ping.go create mode 100755 scripts/buildtestlets.sh diff --git a/.gitignore b/.gitignore index 925635d..0df351b 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ preso_secret/ client/repeattest.sh *.out *.DS_Store* +agent/testing/downloaded_testlets/ diff --git a/Makefile b/Makefile index 0b9318c..59bd975 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,9 @@ build: compile: + ./scripts/buildtestlets.sh + go install ./cmd/... + # Installing testlets ./scripts/gettestlets.sh diff --git a/agent/testing/testlets/_ping b/agent/testing/bashtestlets/notatallping similarity index 100% rename from agent/testing/testlets/_ping rename to agent/testing/bashtestlets/notatallping diff --git a/agent/testing/testlets/ping/ping.go b/agent/testing/testlets/ping/ping.go deleted file mode 100644 index 42cc8d3..0000000 --- a/agent/testing/testlets/ping/ping.go +++ /dev/null @@ -1,97 +0,0 @@ -package toddping - -// NOTE ////////////////// -// -// This is a built-in testlet. Currently, the approach is to have each -// testlet under it's own package, which is explicitly imported under -// the ToDD agent's 'main' package. -// -// Currently, each testlet is stored within the ToDD repo in order to -// vet out the architecture, which means they are awkwardly placed -// underneath the "testlets" directory together. However, this should be -// a temporary holding place, as the main effort around native testlets -// is being implemented so they can be broken out into their own repos. - -import ( - "fmt" - "time" - - log "github.com/Sirupsen/logrus" - - "github.com/Mierdin/todd/agent/testing/testlets" -) - -type PingTestlet struct { - testlets.BaseTestlet -} - -func init() { - - var pt = PingTestlet{} - - // Ensure the RunFunction attribute is set correctly. - // This allows the underlying testlet infrastructure - // to know what function to call at runtime - pt.RunFunction = pt.RunTestlet - - // This is important - register the name of this testlet - // (the name the user will use in a testrun definition) - testlets.Register("ping", &pt) -} - -// RunTestlet implements the core logic of the testlet. Don't worry about running asynchronously, -// that's handled by the infrastructure. -func (p PingTestlet) RunTestlet(target string, args []string, kill chan (bool)) (map[string]string, error) { - - // Get number of pings - count := 3 //TODO(mierdin): need to parse from 'args', or if omitted, use a default value - - log.Error(args) - - var latencies []float32 - var replies int - - // Execute ping once per count - i := 0 - for i < count { - select { - case <-kill: - // Terminating early; return empty metrics - return map[string]string{}, nil - default: - - //log.Debugf("Executing ping #%d", i) - - // Mocked ping logic - latency, replyReceived := pingTemp(count) - - latencies = append(latencies, latency) - - if replyReceived { - replies += 1 - } - - i += 1 - time.Sleep(1000 * time.Millisecond) - - } - } - - // Calculate metrics - var latencyTotal float32 = 0 - for _, value := range latencies { - latencyTotal += value - } - avg_latency_ms := latencyTotal / float32(len(latencies)) - packet_loss := (float32(count) - float32(replies)) / float32(count) - - return map[string]string{ - "avg_latency_ms": fmt.Sprintf("%.2f", avg_latency_ms), - "packet_loss": fmt.Sprintf("%.2f", packet_loss), - }, nil - -} - -func pingTemp(count int) (float32, bool) { - return float32(count) * 4.234, true -} diff --git a/docs/nativetests.rst b/docs/nativetests.rst index e6c04a3..10ec9d2 100644 --- a/docs/nativetests.rst +++ b/docs/nativetests.rst @@ -3,4 +3,18 @@ Native Tests Need to talk about the native tests you've built in, and turn the "testlets" doc into more of a "so you want to build your own, eh?" -Also need to figure out if you want to refer to both native and non-native as "testlets", or maybe reserve that for non-native \ No newline at end of file +Also need to figure out if you want to refer to both native and non-native as "testlets", or maybe reserve that for non-native + + + +Need a design guide outlining some requirements for native testlets: + +* Testlets must honor the "kill" channel passed to the RunTestlet function. If a "true" value is passed into that channel, the testlet must quit immediately. + +* Need to put some specifics together regarding testlets that provide some kind of "server" functionality, kind of like what you've done for custom testlets + +* How do args work in native testlets? It's a bit awkward to continue to use command-line style args in a native testlet but might be necessary to preserve consistency for the user. + +* How to handle vendoring? If a testlet uses a library to run the tests, should the library get vendored with the testlet's repository, or within ToDD proper? Probably the former, but how is it used in that case? Just need to have a strategy here. (You probably vendored such libs in todd proper in order to develop them, so make sure you remove them once they're not needed) + +* How are errors returned from the testlet logic? If a testlet returns an error, how is this handled? \ No newline at end of file diff --git a/scripts/buildtestlets.sh b/scripts/buildtestlets.sh new file mode 100755 index 0000000..2c47c0f --- /dev/null +++ b/scripts/buildtestlets.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +# Copyright 2016 Matt Oswalt. Use or modification of this +# source code is governed by the license provided here: +# https://github.com/mierdin/todd/blob/master/LICENSE + +# This script downloads ToDD testlets prior to compile + +set -e +set -u +set -o pipefail + + +testlets=( + 'https://github.com/Mierdin/todd-nativetestlet-ping.git' + ) + + +rm -rf testlettemp && mkdir testlettemp && cd testlettemp + +for i in "${testlets[@]}" +do + git clone $i +done + +cd .. + +rm -rf agent/testing/downloaded_testlets/ && mkdir agent/testing/downloaded_testlets + +for dir in ./testlettemp/*/ +do + dir=${dir%*/} + cp testlettemp/${dir##*/}/testlet/* agent/testing/downloaded_testlets + #echo ${dir##*/} +done + +rm -rf testlettemp + + + +# rebuild plugins: +# _debug "removing: ${plugin_dir:?}/*" +# rm -rf "${plugin_dir:?}/"* +# mkdir -p "${plugin_dir}" + +# _info "building plugins" +# find "${__proj_dir}/plugin/" -type d -iname "snap-*" -print0 | xargs -0 -n 1 -I{} "${__dir}/build_plugin.sh" {} + +#--------- + + + +# __dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# __proj_dir="$(dirname "$__dir")" + +# # shellcheck source=scripts/common.sh +# . "${__dir}/common.sh" + +# build_dir="${__proj_dir}/build" +# plugin_dir="${build_dir}/plugin" + +# plugin_src_path=$1 +# plugin_name=$(basename "${plugin_src_path}") +# go_build=(go build -a -ldflags "-w") + +# _debug "plugin source: ${plugin_src_path}" +# _info "building ${plugin_name}" + +# (cd "${plugin_src_path}" && "${go_build[@]}" -o "${plugin_dir}/${plugin_name}" . || exit 1) \ No newline at end of file From e837048edae02c13383295ccd039370a9d8a5ea4 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Fri, 19 Aug 2016 12:00:41 -0700 Subject: [PATCH 097/120] Updates to vagrant and make Signed-off-by: Matt Oswalt --- Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Makefile b/Makefile index 59bd975..838f1fe 100644 --- a/Makefile +++ b/Makefile @@ -52,5 +52,10 @@ install: mkdir -p /opt/todd/{agent,server}/assets/{factcollectors,testlets} chmod -R 777 /opt/todd +<<<<<<< 94fdc30dddc0f6844c8fb51d53bdbe543236aaa9 # If on Linux, enable ping testlet functionality (DEPRECATED in favor of granting socket capabilities on testlets) # sysctl -w net.ipv4.ping_group_range="0 0" || echo "Unable to set kernel parameters to allow ping. Some testlets may not work." +======= + # If on Linux, enable ping testlet functionality + sudo sysctl -w net.ipv4.ping_group_range="0 12345" +>>>>>>> Updates to vagrant and make From 93aef6660366e08115268a9ada037e434c94b986 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Fri, 19 Aug 2016 12:03:45 -0700 Subject: [PATCH 098/120] Catch up Signed-off-by: Matt Oswalt --- docs/nativetests.rst | 20 ----------------- scripts/buildtestlets.sh | 46 +++++++++++++++++++++++++++++----------- 2 files changed, 34 insertions(+), 32 deletions(-) delete mode 100644 docs/nativetests.rst diff --git a/docs/nativetests.rst b/docs/nativetests.rst deleted file mode 100644 index 10ec9d2..0000000 --- a/docs/nativetests.rst +++ /dev/null @@ -1,20 +0,0 @@ -Native Tests -================================ - -Need to talk about the native tests you've built in, and turn the "testlets" doc into more of a "so you want to build your own, eh?" - -Also need to figure out if you want to refer to both native and non-native as "testlets", or maybe reserve that for non-native - - - -Need a design guide outlining some requirements for native testlets: - -* Testlets must honor the "kill" channel passed to the RunTestlet function. If a "true" value is passed into that channel, the testlet must quit immediately. - -* Need to put some specifics together regarding testlets that provide some kind of "server" functionality, kind of like what you've done for custom testlets - -* How do args work in native testlets? It's a bit awkward to continue to use command-line style args in a native testlet but might be necessary to preserve consistency for the user. - -* How to handle vendoring? If a testlet uses a library to run the tests, should the library get vendored with the testlet's repository, or within ToDD proper? Probably the former, but how is it used in that case? Just need to have a strategy here. (You probably vendored such libs in todd proper in order to develop them, so make sure you remove them once they're not needed) - -* How are errors returned from the testlet logic? If a testlet returns an error, how is this handled? \ No newline at end of file diff --git a/scripts/buildtestlets.sh b/scripts/buildtestlets.sh index 2c47c0f..0875fb1 100755 --- a/scripts/buildtestlets.sh +++ b/scripts/buildtestlets.sh @@ -12,29 +12,51 @@ set -o pipefail testlets=( - 'https://github.com/Mierdin/todd-nativetestlet-ping.git' + 'github.com/toddproject/todd-nativetestlet-ping' ) -rm -rf testlettemp && mkdir testlettemp && cd testlettemp +#rm -rf testlettemp && mkdir testlettemp && cd testlettemp for i in "${testlets[@]}" do - git clone $i + #echo "Installing $i" + # git clone $i --quiet + go get -d -u $i/... done -cd .. +# cd .. + +# rm -rf agent/testing/downloaded_testlets/ && mkdir agent/testing/downloaded_testlets + +# for dir in ./testlettemp/*/ +# do +# dir=${dir%*/} +# cp testlettemp/${dir##*/}/testlet/* agent/testing/downloaded_testlets +# #echo ${dir##*/} + + + +# testletdir="$(pwd)/$dir" +# #echo $testletdir + +# ln -s $testletdir/vendor/ $testletdir/vendor/src + +# # echo ./testlettemp/todd-nativetestlet-ping + +# # Append this vendor directory to GOPATH +# # TODO need to do some cleanup somewhere to remove this +# if [[ ":$GOPATH:" != *":$testletdir/vendor:"* ]]; then +# echo "export GOPATH=$GOPATH:$testletdir/vendor" +# fi + + +# done + -rm -rf agent/testing/downloaded_testlets/ && mkdir agent/testing/downloaded_testlets -for dir in ./testlettemp/*/ -do - dir=${dir%*/} - cp testlettemp/${dir##*/}/testlet/* agent/testing/downloaded_testlets - #echo ${dir##*/} -done -rm -rf testlettemp +# rm -rf testlettemp From 15536a6f44dff13d716aab2b02316eb4e6cb99c0 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Fri, 19 Aug 2016 14:52:22 -0700 Subject: [PATCH 099/120] Got native testlets working Signed-off-by: Matt Oswalt --- agent/tasks/installtestrun.go | 55 +++++++++++++++--------------- agent/testing/testlets/testlets.go | 19 ++++++----- 2 files changed, 39 insertions(+), 35 deletions(-) diff --git a/agent/tasks/installtestrun.go b/agent/tasks/installtestrun.go index 08594e4..d82a58d 100644 --- a/agent/tasks/installtestrun.go +++ b/agent/tasks/installtestrun.go @@ -43,49 +43,50 @@ func (itt InstallTestRunTask) Run() error { return errors.New("Testlet parameter for this testrun is null") } - // Determine if this is a valid native testlet - _, err := testlets.NewTestlet(itt.Tr.Testlet) + var testlet_path string + // Determine if this is a valid native testlet + //_, err := testlets.NewTestlet(itt.Tr.Testlet) + isNative, newName := testlets.IsNativeTestlet(itt.Tr.Testlet) // Not a native testlet - attempt to run check mode on testlet in filesystem - if err != nil { + if isNative { + // Nothing to do, as we're using a native testlet + log.Infof("%s is a native testlet - installing testrun.", itt.Tr.Testlet) + //itt.Tr.Testlet = newName + testlet_path = newName + } else { // Generate path to testlet and make sure it exists. - testlet_path := fmt.Sprintf("%s/assets/testlets/%s", itt.Config.LocalResources.OptDir, itt.Tr.Testlet) + testlet_path = fmt.Sprintf("%s/assets/testlets/%s", itt.Config.LocalResources.OptDir, itt.Tr.Testlet) if _, err := os.Stat(testlet_path); os.IsNotExist(err) { log.Errorf("Testlet %s does not exist on this agent", itt.Tr.Testlet) return errors.New("Error installing testrun - testlet doesn't exist on this agent.") } + } - // Run the testlet in check mode to verify that everything is okay to run this test - log.Debug("Running testlet in check mode: ", testlet_path) - cmd := exec.Command(testlet_path, "check") - - // Stdout buffer - cmdOutput := &bytes.Buffer{} - // Attach buffer to command - cmd.Stdout = cmdOutput - // Execute collector - cmd.Run() - - // This is probably the best cross-platform way to see if check mode passed. - if strings.Contains(string(cmdOutput.Bytes()), "Check mode PASSED") { - log.Debugf("Check mode for %s passed", testlet_path) - } else { - log.Error("Testlet returned an error during check mode: ", string(cmdOutput.Bytes())) - return errors.New("Testlet returned an error during check mode") - } - - } else { + // Run the testlet in check mode to verify that everything is okay to run this test + log.Debug("Running testlet in check mode: ", testlet_path) + cmd := exec.Command(testlet_path, "check") - // Nothing to do, as we're using a native testlet - log.Infof("%s is a native testlet - installing testrun.", itt.Tr.Testlet) + // Stdout buffer + cmdOutput := &bytes.Buffer{} + // Attach buffer to command + cmd.Stdout = cmdOutput + // Execute collector + cmd.Run() + // This is probably the best cross-platform way to see if check mode passed. + if strings.Contains(string(cmdOutput.Bytes()), "Check mode PASSED") { + log.Debugf("Check mode for %s passed", testlet_path) + } else { + log.Error("Testlet returned an error during check mode: ", string(cmdOutput.Bytes())) + return errors.New("Testlet returned an error during check mode") } // Insert testrun into agent cache var ac = cache.NewAgentCache(itt.Config) - err = ac.InsertTestRun(itt.Tr) + err := ac.InsertTestRun(itt.Tr) if err != nil { log.Error(err) return errors.New("Problem installing test run into agent cache") diff --git a/agent/testing/testlets/testlets.go b/agent/testing/testlets/testlets.go index 4458892..7bcafe1 100644 --- a/agent/testing/testlets/testlets.go +++ b/agent/testing/testlets/testlets.go @@ -12,10 +12,13 @@ import ( ) var ( - testletsMu sync.RWMutex - testlets = make(map[string]Testlet) - done = make(chan error) // Used from within the goroutine to inform the infrastructure it has finished - kill = make(chan bool) // Used from outside the goroutine to inform the goroutine to stop + testletsMu sync.RWMutex + testlets = make(map[string]Testlet) + done = make(chan error) // Used from within the goroutine to inform the infrastructure it has finished + kill = make(chan bool) // Used from outside the goroutine to inform the goroutine to stop + nativeTestlets = map[string]string{ + "ping": "toddping", + } ) // Testlet defines what a testlet should look like if built in native @@ -107,11 +110,11 @@ func (b BaseTestlet) Kill() error { // IsNativeTestlet polls the list of registered native testlets, and returns // true if the referenced name exists -func IsNativeTestlet(name string) bool { - if _, ok := testlets[name]; ok { - return true +func IsNativeTestlet(name string) (bool, string) { + if _, ok := nativeTestlets[name]; ok { + return true, nativeTestlets[name] } else { - return false + return false, "" } } From eb8a3c3cd65bd3c2eb96a2c54336ca7651ce9ee2 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Mon, 22 Aug 2016 23:39:44 -0700 Subject: [PATCH 100/120] Catch up Signed-off-by: Matt Oswalt --- agent/tasks/installtestrun.go | 11 ++- agent/testing/bashtestlets/notatallping | 32 --------- agent/testing/testlets/testlets.go | 34 +++++++-- scripts/buildtestlets.sh | 91 ------------------------- 4 files changed, 33 insertions(+), 135 deletions(-) delete mode 100755 agent/testing/bashtestlets/notatallping delete mode 100755 scripts/buildtestlets.sh diff --git a/agent/tasks/installtestrun.go b/agent/tasks/installtestrun.go index d82a58d..9af412f 100644 --- a/agent/tasks/installtestrun.go +++ b/agent/tasks/installtestrun.go @@ -45,16 +45,15 @@ func (itt InstallTestRunTask) Run() error { var testlet_path string - // Determine if this is a valid native testlet - //_, err := testlets.NewTestlet(itt.Tr.Testlet) + // Determine if this is a native testlet isNative, newName := testlets.IsNativeTestlet(itt.Tr.Testlet) - // Not a native testlet - attempt to run check mode on testlet in filesystem + + // If we're running a native testlet, we want testlet_path to simply be the testlet name + // (since it is a requirement that the native-Go testlets are in the PATH) + // If the testlet is not native, we can get the full path. if isNative { - // Nothing to do, as we're using a native testlet log.Infof("%s is a native testlet - installing testrun.", itt.Tr.Testlet) - //itt.Tr.Testlet = newName testlet_path = newName - } else { // Generate path to testlet and make sure it exists. testlet_path = fmt.Sprintf("%s/assets/testlets/%s", itt.Config.LocalResources.OptDir, itt.Tr.Testlet) diff --git a/agent/testing/bashtestlets/notatallping b/agent/testing/bashtestlets/notatallping deleted file mode 100755 index aed17d4..0000000 --- a/agent/testing/bashtestlets/notatallping +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env bash - -# Copyright 2016 Matt Oswalt. Use or modification of this -# source code is governed by the license provided here: -# https://github.com/Mierdin/todd/blob/master/LICENSE - -check() { - echo "Running in check mode" - command -v ping >/dev/null 2>&1 || { echo >&2 "Ping is not installed. Exiting."; exit 1; } - echo "Check mode PASSED" - exit 0 -} - -if [[ $1 == "check" ]]; - then - check -fi - -# The first argument after the testlet name will ALWAYS be the target. So, $1 is always the target, and all arguments from $2 and up should be passed into the app command -app_args="$@" -app_args=${app_args#$0 } -app_args=${app_args#$1 } - -app_run_command="ping $app_args $1" - -output=$($app_run_command) - -packet_loss=$(echo $output | tr '\n' ' ' | sed -n 's/.* \(.*\)% packet loss.*/\1/p') -avg_latency=$(echo $output | tr '\n' ' ' | sed -n 's/.*dev = .*\/\(.*\)\/.*\/.*/\1/p') - -teslet_data='{"packet_loss_percentage":"'$packet_loss'", "avg_latency_ms":"'$avg_latency'"}' -echo $teslet_data \ No newline at end of file diff --git a/agent/testing/testlets/testlets.go b/agent/testing/testlets/testlets.go index 7bcafe1..ea35bce 100644 --- a/agent/testing/testlets/testlets.go +++ b/agent/testing/testlets/testlets.go @@ -6,16 +6,37 @@ import ( "sort" "sync" "time" - //"sync/atomic" log "github.com/Sirupsen/logrus" ) +// NOTE +// +// Early efforts to build native-Go testlets involved the embedding of testlet logic into the +// ToDD agent itself. As a result, it was important to build some reusable infrastructure so that goroutines +// running testlet code inside the agent could be controlled, and that new testlets could benefit from this +// infrastructure. +// +// Since then, the decision was made to keep testlets as their own separate binaries. Despite this, we can still benefit +// from having them in Go because it is much more cross-platform than bash scripts. +// +// These testlets are in their own repositories, and they do actually use some of the logic below, just not as meaningfully +// and comprehensively as they would have if they were baked in to the agent. Those testlets will still vendor this code +// and leverage the "Testlet" interface so that in the future, if we want to roll these into the todd-agent, those +// testlets will already conform to the standard provided below. + var ( - testletsMu sync.RWMutex - testlets = make(map[string]Testlet) - done = make(chan error) // Used from within the goroutine to inform the infrastructure it has finished - kill = make(chan bool) // Used from outside the goroutine to inform the goroutine to stop + testletsMu sync.RWMutex + testlets = make(map[string]Testlet) + done = make(chan error) // Used from within the goroutine to inform the infrastructure it has finished + kill = make(chan bool) // Used from outside the goroutine to inform the goroutine to stop + + // This map provides name redirection so that the native testlets can use names that don't + // conflict with existing system tools (i.e. using "toddping" instead of "ping") but users + // can still refer to the testlets using simple names. + // + // In short, users refer to the testlet by and this map will redirect to the + // actual binary name nativeTestlets = map[string]string{ "ping": "toddping", } @@ -73,7 +94,8 @@ func (b BaseTestlet) Run(target string, args []string, timeLimit int) (map[strin // kill = make(chan bool) // TODO(mierdin): Based on experimentation, this will keep running even if this function returns. - // Need to be sure about how this ends. Also might want to evaluate the same for the existing non-native model, likely has the same issue + // Need to be sure about how this ends. Also might want to evaluate the same for the existing + // non-native model, likely has the same issue go func() { theseMetrics, err := b.RunFunction(target, args, kill) metrics = theseMetrics //TODO(mierdin): Gross. diff --git a/scripts/buildtestlets.sh b/scripts/buildtestlets.sh deleted file mode 100755 index 0875fb1..0000000 --- a/scripts/buildtestlets.sh +++ /dev/null @@ -1,91 +0,0 @@ -#!/bin/bash - -# Copyright 2016 Matt Oswalt. Use or modification of this -# source code is governed by the license provided here: -# https://github.com/mierdin/todd/blob/master/LICENSE - -# This script downloads ToDD testlets prior to compile - -set -e -set -u -set -o pipefail - - -testlets=( - 'github.com/toddproject/todd-nativetestlet-ping' - ) - - -#rm -rf testlettemp && mkdir testlettemp && cd testlettemp - -for i in "${testlets[@]}" -do - #echo "Installing $i" - # git clone $i --quiet - go get -d -u $i/... -done - -# cd .. - -# rm -rf agent/testing/downloaded_testlets/ && mkdir agent/testing/downloaded_testlets - -# for dir in ./testlettemp/*/ -# do -# dir=${dir%*/} -# cp testlettemp/${dir##*/}/testlet/* agent/testing/downloaded_testlets -# #echo ${dir##*/} - - - -# testletdir="$(pwd)/$dir" -# #echo $testletdir - -# ln -s $testletdir/vendor/ $testletdir/vendor/src - -# # echo ./testlettemp/todd-nativetestlet-ping - -# # Append this vendor directory to GOPATH -# # TODO need to do some cleanup somewhere to remove this -# if [[ ":$GOPATH:" != *":$testletdir/vendor:"* ]]; then -# echo "export GOPATH=$GOPATH:$testletdir/vendor" -# fi - - -# done - - - - -# rm -rf testlettemp - - - -# rebuild plugins: -# _debug "removing: ${plugin_dir:?}/*" -# rm -rf "${plugin_dir:?}/"* -# mkdir -p "${plugin_dir}" - -# _info "building plugins" -# find "${__proj_dir}/plugin/" -type d -iname "snap-*" -print0 | xargs -0 -n 1 -I{} "${__dir}/build_plugin.sh" {} - -#--------- - - - -# __dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -# __proj_dir="$(dirname "$__dir")" - -# # shellcheck source=scripts/common.sh -# . "${__dir}/common.sh" - -# build_dir="${__proj_dir}/build" -# plugin_dir="${build_dir}/plugin" - -# plugin_src_path=$1 -# plugin_name=$(basename "${plugin_src_path}") -# go_build=(go build -a -ldflags "-w") - -# _debug "plugin source: ${plugin_src_path}" -# _info "building ${plugin_name}" - -# (cd "${plugin_src_path}" && "${go_build[@]}" -o "${plugin_dir}/${plugin_name}" . || exit 1) \ No newline at end of file From 2c87bf3cf08a0399add3cb73a2f4de9f2dd70b59 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Tue, 23 Aug 2016 22:43:25 -0700 Subject: [PATCH 101/120] Moved testlets outside to the testing package Signed-off-by: Matt Oswalt --- agent/testing/{testlets => }/testlets.go | 48 ++++++++++++------------ 1 file changed, 23 insertions(+), 25 deletions(-) rename agent/testing/{testlets => }/testlets.go (79%) diff --git a/agent/testing/testlets/testlets.go b/agent/testing/testlets.go similarity index 79% rename from agent/testing/testlets/testlets.go rename to agent/testing/testlets.go index ea35bce..dc4bb51 100644 --- a/agent/testing/testlets/testlets.go +++ b/agent/testing/testlets.go @@ -1,4 +1,12 @@ -package testlets +/* + ToDD task - set keyvalue pair in cache + + Copyright 2016 Matt Oswalt. Use or modification of this + source code is governed by the license provided here: + https://github.com/Mierdin/todd/blob/master/LICENSE +*/ + +package testing import ( "errors" @@ -17,13 +25,14 @@ import ( // running testlet code inside the agent could be controlled, and that new testlets could benefit from this // infrastructure. // -// Since then, the decision was made to keep testlets as their own separate binaries. Despite this, we can still benefit -// from having them in Go because it is much more cross-platform than bash scripts. +// Since then, the decision was made to keep testlets as their own separate binaries. // // These testlets are in their own repositories, and they do actually use some of the logic below, just not as meaningfully -// and comprehensively as they would have if they were baked in to the agent. Those testlets will still vendor this code -// and leverage the "Testlet" interface so that in the future, if we want to roll these into the todd-agent, those -// testlets will already conform to the standard provided below. +// and comprehensively as they would have if they were baked in to the agent. The development standard for all "blessed" +// testlets will still ensure that they use this interface, so that if we decide to bake them into the agent in the future, +// they'll already conform. +// +// (The vast majority of this code was inspired by the database drivers implementation in the stdlib) var ( testletsMu sync.RWMutex @@ -69,7 +78,6 @@ type Testlet interface { // without worrying about things like managing goroutines or channels. That's all // managed by the "Run" or "Kill" functions RunTestlet(string, []string, chan bool) (map[string]string, error) - // TODO(mierdin): is this really the best name for it? Maybe something that's less confusing, less like "Run" // All testlets must be able to stop operation when sent a Kill command. Kill() error @@ -89,16 +97,12 @@ func (b BaseTestlet) Run(target string, args []string, timeLimit int) (map[strin var metrics map[string]string - // TODO(mierdin): ensure channel is nil - // done = make(chan error) - // kill = make(chan bool) + // Ensure control channels are empty + done := make(chan error) + kill := make(chan bool) - // TODO(mierdin): Based on experimentation, this will keep running even if this function returns. - // Need to be sure about how this ends. Also might want to evaluate the same for the existing - // non-native model, likely has the same issue go func() { - theseMetrics, err := b.RunFunction(target, args, kill) - metrics = theseMetrics //TODO(mierdin): Gross. + metrics, err := b.RunFunction(target, args, kill) done <- err }() @@ -112,7 +116,7 @@ func (b BaseTestlet) Run(target string, args []string, timeLimit int) (map[strin case err := <-done: if err != nil { - return map[string]string{}, errors.New("testlet error") // TODO(mierdin): elaborate? + return map[string]string{}, errors.New("testlet error") } else { log.Debugf("Testlet completed without error") return metrics, nil @@ -120,13 +124,10 @@ func (b BaseTestlet) Run(target string, args []string, timeLimit int) (map[strin } } +// Kill is currently unimplemented. This will have to be coordinated with "Run". Basically +// you need a way to kill this testlet (and that's really only possible when running +// async) Probably just want to set the channel to something so the select within "Run" will execute func (b BaseTestlet) Kill() error { - // TODO (mierdin): This will have to be coordinated with the task above. Basically - // you need a way to kill this testlet (and that's really only possible when running - // async) - - // Probably just want to set the channel to something so the select within "Run" will execute - return nil } @@ -144,9 +145,6 @@ func IsNativeTestlet(name string) (bool, string) { func NewTestlet(name string) (Testlet, error) { if testlet, ok := testlets[name]; ok { - - // testlet.runFunction = testlet.run - return testlet, nil } else { return nil, errors.New( From ba132d8587d22807fe2a9291401e1c56fb235d3b Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Sun, 18 Sep 2016 02:06:28 -0700 Subject: [PATCH 102/120] Updated testlets.go Signed-off-by: Matt Oswalt --- agent/testing/testlets.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/agent/testing/testlets.go b/agent/testing/testlets.go index dc4bb51..0a5f2c6 100644 --- a/agent/testing/testlets.go +++ b/agent/testing/testlets.go @@ -103,6 +103,14 @@ func (b BaseTestlet) Run(target string, args []string, timeLimit int) (map[strin go func() { metrics, err := b.RunFunction(target, args, kill) +<<<<<<< 5056f30dcb3a2cf7a20e251b620c8220217f2f4b:agent/testing/testlets.go +======= + + // TODO(mierdin): avoiding a "declared and not used" error for now + // If this code is ever actually used, it should be modified to make "done" a channel that returns the metrics, so it's actually used (just an idea) + log.Error(metrics) + +>>>>>>> Updated testlets.go:agent/testing/testlets/testlets.go done <- err }() From 4e6a2ff10a7269e2520e601dd2891868ac628867 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Mon, 19 Sep 2016 00:56:17 -0700 Subject: [PATCH 103/120] Various moves Signed-off-by: Matt Oswalt --- agent/testing/testing.go | 118 ++++++++++++++++++--------------------- 1 file changed, 54 insertions(+), 64 deletions(-) diff --git a/agent/testing/testing.go b/agent/testing/testing.go index 9155e9d..58489ed 100644 --- a/agent/testing/testing.go +++ b/agent/testing/testing.go @@ -1,78 +1,68 @@ /* - ToDD testing package + ToDD Agent - Test Reporting - Contains infrastructure running testlets as well as maintaining - conformance for other native-Go testlet projects + This file contains functions that watch the agent cache and upload test data when present. - Copyright 2016 Matt Oswalt. Use or modification of this - source code is governed by the license provided here: - https://github.com/Mierdin/todd/blob/master/LICENSE + Copyright 2016 Matt Oswalt. Use or modification of this + source code is governed by the license provided here: + https://github.com/Mierdin/todd/blob/master/LICENSE */ package testing -var ( +import ( + "errors" + "time" - // This map provides name redirection so that the native testlets can use names that don't - // conflict with existing system tools (i.e. using "toddping" instead of "ping") but users - // can still refer to the testlets using simple names. - // - // In short, users refer to the testlet by and this map will redirect to the - // actual binary name - nativeTestlets = map[string]string{ - "ping": "toddping", - } + log "github.com/Sirupsen/logrus" + + "github.com/Mierdin/todd/agent/cache" + "github.com/Mierdin/todd/agent/responses" + "github.com/Mierdin/todd/comms" + "github.com/Mierdin/todd/config" ) -// Testlet defines what a testlet should look like if built in native -// go and compiled with the agent -type Testlet interface { - - // Run is the "workflow" function for a testlet. All testing takes place here - // (or in a function called within) - // - // Params are - // target (string) - // args ([]string) - // timeLimit (int in seconds) - // - // Returns: - // metrics (map[string]string) - // (name of metric is key, value is metric value) - Run(string, []string, int) (map[string]string, error) -} +// WatchForFinishedTestRuns simply watches the local cache for any test runs that have test data. +// It will periodically look at the table and send any present test data back to the server as a response. +// When the server has successfully received this data, it will send a task back to this specific agent +// to delete this row from the cache. +func WatchForFinishedTestRuns(cfg config.Config) error { -// NOTE -// -// Early efforts to build native-Go testlets involved the embedding of testlet logic into the -// ToDD agent itself. As a result, it was important to build some reusable infrastructure so that goroutines -// running testlet code within the agent could be controlled, and that new testlets could benefit from this -// infrastructure. -// -// Since then, the decision was made to keep testlets as their own separate binaries. -// -// These testlets are in their own repositories, and they do actually use some of the logic below, just not as meaningfully -// and comprehensively as they would have if they were baked in to the agent. The development standard for all "blessed" -// testlets will still ensure that they use this interface, so that if we decide to bake them into the agent in the future, -// they'll already conform. -// -// (The vast majority of this code was inspired by the database drivers implementation in the stdlib) - -type rtfunc func(target string, args []string, timeout int) (map[string]string, error) - -type BaseTestlet struct { - - // rtfunc is a type that will store our RunTestlet function. It is the responsibility - // of the "child" testlet to set this value upon creation - RunFunction rtfunc -} + var ac = cache.NewAgentCache(cfg) + + agentUuid := ac.GetKeyValue("uuid") + + for { + + time.Sleep(5000 * time.Millisecond) + + testruns, err := ac.GetFinishedTestRuns() + if err != nil { + log.Error("Problem retrieving finished test runs") + return errors.New("Problem retrieving finished test runs") + } + + for testUuid, testData := range testruns { + + log.Debug("Found ripe testrun: ", testUuid) + + var utdr = responses.UploadTestDataResponse{ + TestUuid: testUuid, + TestData: testData, + } + utdr.AgentUuid = agentUuid + utdr.Type = "TestData" //TODO(mierdin): This is an extra step. Maybe a factory function for the task could help here? + + tc, err := comms.NewToDDComms(cfg) + if err != nil { + return err + } + tc.CommsPackage.SendResponse(utdr) + + } -// IsNativeTestlet polls the list of registered native testlets, and returns -// true if the referenced name exists -func IsNativeTestlet(name string) (bool, string) { - if _, ok := nativeTestlets[name]; ok { - return true, nativeTestlets[name] - } else { - return false, "" } + + return nil + } From ecab9156d2942b52d2fb3dab7227ac5de4a7e888 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Mon, 19 Sep 2016 22:49:40 -0700 Subject: [PATCH 104/120] removed all old work from testlets (moved to archive) Signed-off-by: Matt Oswalt --- agent/testing/testlets.go | 51 +++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/agent/testing/testlets.go b/agent/testing/testlets.go index 0a5f2c6..0ad300c 100644 --- a/agent/testing/testlets.go +++ b/agent/testing/testlets.go @@ -9,13 +9,8 @@ package testing import ( - "errors" - "fmt" - "sort" "sync" - "time" - - log "github.com/Sirupsen/logrus" + // log "github.com/Sirupsen/logrus" ) // NOTE @@ -55,8 +50,8 @@ var ( // go and compiled with the agent type Testlet interface { - // Run is the "workflow" function for a testlet. It handles running - // the RunTestlet function asynchronously and managing the state therein. + // Run is the "workflow" function for a testlet. All testing takes place here + // (or in a function called within) // // Params are // target (string) @@ -64,26 +59,28 @@ type Testlet interface { // timeLimit (int in seconds) // // Returns: - // metrics (map[string]interface{}) + // metrics (map[string]string) // (name of metric is key, value is metric value) - // - // Keep as much logic out of here as possible. All native testlets - // must support a "Kill" method, so it's best to implement core testlet - // logic in a separate function so that the Run and Kill commands can manage - // execution of that logic in a goroutine Run(string, []string, int) (map[string]string, error) - - // RunTestlet is designed to be the one-stop shop for testlet logic. - // The developer of a native testlet just needs to implement the testlet logic here, - // without worrying about things like managing goroutines or channels. That's all - // managed by the "Run" or "Kill" functions - RunTestlet(string, []string, chan bool) (map[string]string, error) - - // All testlets must be able to stop operation when sent a Kill command. - Kill() error } -type rtfunc func(target string, args []string, kill chan bool) (map[string]string, error) +// NOTE +// +// Early efforts to build native-Go testlets involved the embedding of testlet logic into the +// ToDD agent itself. As a result, it was important to build some reusable infrastructure so that goroutines +// running testlet code within the agent could be controlled, and that new testlets could benefit from this +// infrastructure. +// +// Since then, the decision was made to keep testlets as their own separate binaries. +// +// These testlets are in their own repositories, and they do actually use some of the logic below, just not as meaningfully +// and comprehensively as they would have if they were baked in to the agent. The development standard for all "blessed" +// testlets will still ensure that they use this interface, so that if we decide to bake them into the agent in the future, +// they'll already conform. +// +// (The vast majority of this code was inspired by the database drivers implementation in the stdlib) + +type rtfunc func(target string, args []string, timeout int) (map[string]string, error) type BaseTestlet struct { @@ -92,6 +89,7 @@ type BaseTestlet struct { RunFunction rtfunc } +<<<<<<< 287aa03c127fa5a60db0684c4e4340d6cec19f62:agent/testing/testlets.go // Run takes care of running the testlet function and managing it's operation given the parameters provided func (b BaseTestlet) Run(target string, args []string, timeLimit int) (map[string]string, error) { @@ -139,6 +137,8 @@ func (b BaseTestlet) Kill() error { return nil } +======= +>>>>>>> removed all old work from testlets (moved to archive):agent/testing/testlets/testlets.go // IsNativeTestlet polls the list of registered native testlets, and returns // true if the referenced name exists func IsNativeTestlet(name string) (bool, string) { @@ -148,6 +148,7 @@ func IsNativeTestlet(name string) (bool, string) { return false, "" } } +<<<<<<< 287aa03c127fa5a60db0684c4e4340d6cec19f62:agent/testing/testlets.go //NewTestlet produces a new testlet based on the "name" param func NewTestlet(name string) (Testlet, error) { @@ -195,3 +196,5 @@ func Testlets() []string { sort.Strings(list) return list } +======= +>>>>>>> removed all old work from testlets (moved to archive):agent/testing/testlets/testlets.go From 5db5c0dfc5259074c6c67d069b27e48f2ace4806 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Mon, 19 Sep 2016 23:03:05 -0700 Subject: [PATCH 105/120] Reorganized testing logic Signed-off-by: Matt Oswalt --- agent/tasks/installtestrun.go | 2 +- agent/testing/testing.go | 118 ++++++++++++++++++---------------- 2 files changed, 65 insertions(+), 55 deletions(-) diff --git a/agent/tasks/installtestrun.go b/agent/tasks/installtestrun.go index 9af412f..9a783d4 100644 --- a/agent/tasks/installtestrun.go +++ b/agent/tasks/installtestrun.go @@ -46,7 +46,7 @@ func (itt InstallTestRunTask) Run() error { var testlet_path string // Determine if this is a native testlet - isNative, newName := testlets.IsNativeTestlet(itt.Tr.Testlet) + isNative, newName := testing.IsNativeTestlet(itt.Tr.Testlet) // If we're running a native testlet, we want testlet_path to simply be the testlet name // (since it is a requirement that the native-Go testlets are in the PATH) diff --git a/agent/testing/testing.go b/agent/testing/testing.go index 58489ed..9155e9d 100644 --- a/agent/testing/testing.go +++ b/agent/testing/testing.go @@ -1,68 +1,78 @@ /* - ToDD Agent - Test Reporting + ToDD testing package - This file contains functions that watch the agent cache and upload test data when present. + Contains infrastructure running testlets as well as maintaining + conformance for other native-Go testlet projects - Copyright 2016 Matt Oswalt. Use or modification of this - source code is governed by the license provided here: - https://github.com/Mierdin/todd/blob/master/LICENSE + Copyright 2016 Matt Oswalt. Use or modification of this + source code is governed by the license provided here: + https://github.com/Mierdin/todd/blob/master/LICENSE */ package testing -import ( - "errors" - "time" +var ( - log "github.com/Sirupsen/logrus" - - "github.com/Mierdin/todd/agent/cache" - "github.com/Mierdin/todd/agent/responses" - "github.com/Mierdin/todd/comms" - "github.com/Mierdin/todd/config" + // This map provides name redirection so that the native testlets can use names that don't + // conflict with existing system tools (i.e. using "toddping" instead of "ping") but users + // can still refer to the testlets using simple names. + // + // In short, users refer to the testlet by and this map will redirect to the + // actual binary name + nativeTestlets = map[string]string{ + "ping": "toddping", + } ) -// WatchForFinishedTestRuns simply watches the local cache for any test runs that have test data. -// It will periodically look at the table and send any present test data back to the server as a response. -// When the server has successfully received this data, it will send a task back to this specific agent -// to delete this row from the cache. -func WatchForFinishedTestRuns(cfg config.Config) error { - - var ac = cache.NewAgentCache(cfg) - - agentUuid := ac.GetKeyValue("uuid") - - for { - - time.Sleep(5000 * time.Millisecond) - - testruns, err := ac.GetFinishedTestRuns() - if err != nil { - log.Error("Problem retrieving finished test runs") - return errors.New("Problem retrieving finished test runs") - } - - for testUuid, testData := range testruns { - - log.Debug("Found ripe testrun: ", testUuid) - - var utdr = responses.UploadTestDataResponse{ - TestUuid: testUuid, - TestData: testData, - } - utdr.AgentUuid = agentUuid - utdr.Type = "TestData" //TODO(mierdin): This is an extra step. Maybe a factory function for the task could help here? - - tc, err := comms.NewToDDComms(cfg) - if err != nil { - return err - } - tc.CommsPackage.SendResponse(utdr) +// Testlet defines what a testlet should look like if built in native +// go and compiled with the agent +type Testlet interface { + + // Run is the "workflow" function for a testlet. All testing takes place here + // (or in a function called within) + // + // Params are + // target (string) + // args ([]string) + // timeLimit (int in seconds) + // + // Returns: + // metrics (map[string]string) + // (name of metric is key, value is metric value) + Run(string, []string, int) (map[string]string, error) +} - } +// NOTE +// +// Early efforts to build native-Go testlets involved the embedding of testlet logic into the +// ToDD agent itself. As a result, it was important to build some reusable infrastructure so that goroutines +// running testlet code within the agent could be controlled, and that new testlets could benefit from this +// infrastructure. +// +// Since then, the decision was made to keep testlets as their own separate binaries. +// +// These testlets are in their own repositories, and they do actually use some of the logic below, just not as meaningfully +// and comprehensively as they would have if they were baked in to the agent. The development standard for all "blessed" +// testlets will still ensure that they use this interface, so that if we decide to bake them into the agent in the future, +// they'll already conform. +// +// (The vast majority of this code was inspired by the database drivers implementation in the stdlib) + +type rtfunc func(target string, args []string, timeout int) (map[string]string, error) + +type BaseTestlet struct { + + // rtfunc is a type that will store our RunTestlet function. It is the responsibility + // of the "child" testlet to set this value upon creation + RunFunction rtfunc +} +// IsNativeTestlet polls the list of registered native testlets, and returns +// true if the referenced name exists +func IsNativeTestlet(name string) (bool, string) { + if _, ok := nativeTestlets[name]; ok { + return true, nativeTestlets[name] + } else { + return false, "" } - - return nil - } From 318a5600bac7b28c4327d23f9f7bab40b9206ace Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Tue, 20 Sep 2016 15:06:44 -0700 Subject: [PATCH 106/120] removed sudo from sysctl command in makefile Signed-off-by: Matt Oswalt --- Makefile | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Makefile b/Makefile index 838f1fe..c7b44b2 100644 --- a/Makefile +++ b/Makefile @@ -51,11 +51,3 @@ install: if ! [ "etc" -ef "/etc/todd" ]; then mkdir -p /etc/todd && cp -f ./etc/{agent,server}.cfg /etc/todd/; fi mkdir -p /opt/todd/{agent,server}/assets/{factcollectors,testlets} chmod -R 777 /opt/todd - -<<<<<<< 94fdc30dddc0f6844c8fb51d53bdbe543236aaa9 - # If on Linux, enable ping testlet functionality (DEPRECATED in favor of granting socket capabilities on testlets) - # sysctl -w net.ipv4.ping_group_range="0 0" || echo "Unable to set kernel parameters to allow ping. Some testlets may not work." -======= - # If on Linux, enable ping testlet functionality - sudo sysctl -w net.ipv4.ping_group_range="0 12345" ->>>>>>> Updates to vagrant and make From 66f43cf20fe9b11330cadeceea5636df6eb4bd35 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Thu, 22 Sep 2016 14:12:59 -0700 Subject: [PATCH 107/120] Made capabilities script exec Signed-off-by: Matt Oswalt From a91ccfa57118a4b1e694cb38d38863e7f5875bb5 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Thu, 22 Sep 2016 15:09:12 -0700 Subject: [PATCH 108/120] Added some comments about install process Signed-off-by: Matt Oswalt --- Makefile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Makefile b/Makefile index c7b44b2..aa6658c 100644 --- a/Makefile +++ b/Makefile @@ -51,3 +51,9 @@ install: if ! [ "etc" -ef "/etc/todd" ]; then mkdir -p /etc/todd && cp -f ./etc/{agent,server}.cfg /etc/todd/; fi mkdir -p /opt/todd/{agent,server}/assets/{factcollectors,testlets} chmod -R 777 /opt/todd +<<<<<<< 66f43cf20fe9b11330cadeceea5636df6eb4bd35 +======= + + # If on Linux, enable ping testlet functionality (DEPRECATED in favor of granting socket capabilities on testlets) + # sysctl -w net.ipv4.ping_group_range="0 0" || echo "Unable to set kernel parameters to allow ping. Some testlets may not work." +>>>>>>> Added some comments about install process From 9a5a09bf1bb1a1650ce79a1c5fb9708b25e3fe96 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Thu, 22 Sep 2016 16:50:03 -0700 Subject: [PATCH 109/120] Removed extra crap from rebase Signed-off-by: Matt Oswalt --- agent/testing/testlets.go | 200 -------------------------------------- 1 file changed, 200 deletions(-) delete mode 100644 agent/testing/testlets.go diff --git a/agent/testing/testlets.go b/agent/testing/testlets.go deleted file mode 100644 index 0ad300c..0000000 --- a/agent/testing/testlets.go +++ /dev/null @@ -1,200 +0,0 @@ -/* - ToDD task - set keyvalue pair in cache - - Copyright 2016 Matt Oswalt. Use or modification of this - source code is governed by the license provided here: - https://github.com/Mierdin/todd/blob/master/LICENSE -*/ - -package testing - -import ( - "sync" - // log "github.com/Sirupsen/logrus" -) - -// NOTE -// -// Early efforts to build native-Go testlets involved the embedding of testlet logic into the -// ToDD agent itself. As a result, it was important to build some reusable infrastructure so that goroutines -// running testlet code inside the agent could be controlled, and that new testlets could benefit from this -// infrastructure. -// -// Since then, the decision was made to keep testlets as their own separate binaries. -// -// These testlets are in their own repositories, and they do actually use some of the logic below, just not as meaningfully -// and comprehensively as they would have if they were baked in to the agent. The development standard for all "blessed" -// testlets will still ensure that they use this interface, so that if we decide to bake them into the agent in the future, -// they'll already conform. -// -// (The vast majority of this code was inspired by the database drivers implementation in the stdlib) - -var ( - testletsMu sync.RWMutex - testlets = make(map[string]Testlet) - done = make(chan error) // Used from within the goroutine to inform the infrastructure it has finished - kill = make(chan bool) // Used from outside the goroutine to inform the goroutine to stop - - // This map provides name redirection so that the native testlets can use names that don't - // conflict with existing system tools (i.e. using "toddping" instead of "ping") but users - // can still refer to the testlets using simple names. - // - // In short, users refer to the testlet by and this map will redirect to the - // actual binary name - nativeTestlets = map[string]string{ - "ping": "toddping", - } -) - -// Testlet defines what a testlet should look like if built in native -// go and compiled with the agent -type Testlet interface { - - // Run is the "workflow" function for a testlet. All testing takes place here - // (or in a function called within) - // - // Params are - // target (string) - // args ([]string) - // timeLimit (int in seconds) - // - // Returns: - // metrics (map[string]string) - // (name of metric is key, value is metric value) - Run(string, []string, int) (map[string]string, error) -} - -// NOTE -// -// Early efforts to build native-Go testlets involved the embedding of testlet logic into the -// ToDD agent itself. As a result, it was important to build some reusable infrastructure so that goroutines -// running testlet code within the agent could be controlled, and that new testlets could benefit from this -// infrastructure. -// -// Since then, the decision was made to keep testlets as their own separate binaries. -// -// These testlets are in their own repositories, and they do actually use some of the logic below, just not as meaningfully -// and comprehensively as they would have if they were baked in to the agent. The development standard for all "blessed" -// testlets will still ensure that they use this interface, so that if we decide to bake them into the agent in the future, -// they'll already conform. -// -// (The vast majority of this code was inspired by the database drivers implementation in the stdlib) - -type rtfunc func(target string, args []string, timeout int) (map[string]string, error) - -type BaseTestlet struct { - - // rtfunc is a type that will store our RunTestlet function. It is the responsibility - // of the "child" testlet to set this value upon creation - RunFunction rtfunc -} - -<<<<<<< 287aa03c127fa5a60db0684c4e4340d6cec19f62:agent/testing/testlets.go -// Run takes care of running the testlet function and managing it's operation given the parameters provided -func (b BaseTestlet) Run(target string, args []string, timeLimit int) (map[string]string, error) { - - var metrics map[string]string - - // Ensure control channels are empty - done := make(chan error) - kill := make(chan bool) - - go func() { - metrics, err := b.RunFunction(target, args, kill) -<<<<<<< 5056f30dcb3a2cf7a20e251b620c8220217f2f4b:agent/testing/testlets.go -======= - - // TODO(mierdin): avoiding a "declared and not used" error for now - // If this code is ever actually used, it should be modified to make "done" a channel that returns the metrics, so it's actually used (just an idea) - log.Error(metrics) - ->>>>>>> Updated testlets.go:agent/testing/testlets/testlets.go - done <- err - }() - - // This select statement will block until one of these two conditions are met: - // - The testlet finishes, in which case the channel "done" will be receive a value - // - The configured time limit is exceeded (expected for testlets running in server mode) - select { - case <-time.After(time.Duration(timeLimit) * time.Second): - log.Debug("Successfully killed ") - return map[string]string{}, nil - - case err := <-done: - if err != nil { - return map[string]string{}, errors.New("testlet error") - } else { - log.Debugf("Testlet completed without error") - return metrics, nil - } - } -} - -// Kill is currently unimplemented. This will have to be coordinated with "Run". Basically -// you need a way to kill this testlet (and that's really only possible when running -// async) Probably just want to set the channel to something so the select within "Run" will execute -func (b BaseTestlet) Kill() error { - return nil -} - -======= ->>>>>>> removed all old work from testlets (moved to archive):agent/testing/testlets/testlets.go -// IsNativeTestlet polls the list of registered native testlets, and returns -// true if the referenced name exists -func IsNativeTestlet(name string) (bool, string) { - if _, ok := nativeTestlets[name]; ok { - return true, nativeTestlets[name] - } else { - return false, "" - } -} -<<<<<<< 287aa03c127fa5a60db0684c4e4340d6cec19f62:agent/testing/testlets.go - -//NewTestlet produces a new testlet based on the "name" param -func NewTestlet(name string) (Testlet, error) { - - if testlet, ok := testlets[name]; ok { - return testlet, nil - } else { - return nil, errors.New( - fmt.Sprintf("'%s' not currently supported as a native testlet"), - ) - } -} - -// Register makes a testlet available by the provided name. -// If Register is called twice with the same name or if testlet is nil, -// it will return an error -func Register(name string, testlet Testlet) error { - testletsMu.Lock() - defer testletsMu.Unlock() - if testlet == nil { - return errors.New("Register testlet is nil") - } - if _, dup := testlets[name]; dup { - return errors.New("Register called twice for testlet " + name) - } - testlets[name] = testlet - return nil -} - -func unregisterAllTestlets() { - testletsMu.Lock() - defer testletsMu.Unlock() - // For tests. - testlets = make(map[string]Testlet) -} - -// Testlets returns a sorted list of the names of the registered testlets. -func Testlets() []string { - testletsMu.RLock() - defer testletsMu.RUnlock() - var list []string - for name := range testlets { - list = append(list, name) - } - sort.Strings(list) - return list -} -======= ->>>>>>> removed all old work from testlets (moved to archive):agent/testing/testlets/testlets.go From a04a6c9cbdac1ebb2fce5c5e1c5c4fb5286af581 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Thu, 22 Sep 2016 16:56:34 -0700 Subject: [PATCH 110/120] Moved capabilities script to 'compile' Signed-off-by: Matt Oswalt --- Makefile | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Makefile b/Makefile index aa6658c..39b94a1 100644 --- a/Makefile +++ b/Makefile @@ -12,11 +12,9 @@ build: compile: - ./scripts/buildtestlets.sh - go install ./cmd/... - # Installing testlets ./scripts/gettestlets.sh + ./scripts/set-testlet-capabilities.sh # Installing ToDD go install ./cmd/... @@ -44,9 +42,6 @@ start: compile install: - # Set testlet capabilities - ./scripts/set-testlet-capabilities.sh - # Copy configs if etc and /etc/todd aren't linked if ! [ "etc" -ef "/etc/todd" ]; then mkdir -p /etc/todd && cp -f ./etc/{agent,server}.cfg /etc/todd/; fi mkdir -p /opt/todd/{agent,server}/assets/{factcollectors,testlets} From 4163110c420d9556acb610135f1a5bd96c8917bc Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Thu, 22 Sep 2016 17:00:02 -0700 Subject: [PATCH 111/120] Removed old sysctl command from Makefile Signed-off-by: Matt Oswalt --- Makefile | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 39b94a1..c2246aa 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,6 @@ compile: # Installing testlets ./scripts/gettestlets.sh - ./scripts/set-testlet-capabilities.sh # Installing ToDD go install ./cmd/... @@ -42,13 +41,10 @@ start: compile install: + # Set capabilities on testlets + ./scripts/set-testlet-capabilities.sh + # Copy configs if etc and /etc/todd aren't linked if ! [ "etc" -ef "/etc/todd" ]; then mkdir -p /etc/todd && cp -f ./etc/{agent,server}.cfg /etc/todd/; fi mkdir -p /opt/todd/{agent,server}/assets/{factcollectors,testlets} chmod -R 777 /opt/todd -<<<<<<< 66f43cf20fe9b11330cadeceea5636df6eb4bd35 -======= - - # If on Linux, enable ping testlet functionality (DEPRECATED in favor of granting socket capabilities on testlets) - # sysctl -w net.ipv4.ping_group_range="0 0" || echo "Unable to set kernel parameters to allow ping. Some testlets may not work." ->>>>>>> Added some comments about install process From cf5da3374d5263987501097000817b5f18b84dfe Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Thu, 22 Sep 2016 17:00:16 -0700 Subject: [PATCH 112/120] Added comment about folder redir to Vagrantfile Signed-off-by: Matt Oswalt --- Vagrantfile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Vagrantfile b/Vagrantfile index dc26d0e..6f13a6e 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -7,15 +7,17 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.box = "trusty64" config.vm.box_url = "http://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-amd64-vagrant-disk1.box" config.vm.synced_folder '.', '/home/vagrant/go/src/github.com/Mierdin/todd', nfs: true - config.vm.synced_folder '../../toddproject/todd-nativetestlet-ping', '/home/vagrant/go/src/github.com/toddproject/todd-nativetestlet-ping', nfs: true + # This is a temporary measure. Once https://github.com/Mierdin/todd/issues/72 is addressed, we can go back + # to a single folder being redirected (namely toddproject). For now, since ToDD is still in "Mierdin", + # we need to do it this way. + config.vm.synced_folder '../../toddproject/todd-nativetestlet-ping', '/home/vagrant/go/src/github.com/toddproject/todd-nativetestlet-ping', nfs: true config.vm.provider "virtualbox" do |v| v.memory = 2048 v.cpus = 2 end - config.vm.define "todddev" do |todddev| todddev.vm.host_name = "todddev" From 5a9759872c14f5d789b685a2f12dcfba6a27beb5 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Thu, 22 Sep 2016 19:07:19 -0700 Subject: [PATCH 113/120] Removed unnecessary comment block Signed-off-by: Matt Oswalt --- agent/testing/testing.go | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/agent/testing/testing.go b/agent/testing/testing.go index 9155e9d..2485dda 100644 --- a/agent/testing/testing.go +++ b/agent/testing/testing.go @@ -42,22 +42,6 @@ type Testlet interface { Run(string, []string, int) (map[string]string, error) } -// NOTE -// -// Early efforts to build native-Go testlets involved the embedding of testlet logic into the -// ToDD agent itself. As a result, it was important to build some reusable infrastructure so that goroutines -// running testlet code within the agent could be controlled, and that new testlets could benefit from this -// infrastructure. -// -// Since then, the decision was made to keep testlets as their own separate binaries. -// -// These testlets are in their own repositories, and they do actually use some of the logic below, just not as meaningfully -// and comprehensively as they would have if they were baked in to the agent. The development standard for all "blessed" -// testlets will still ensure that they use this interface, so that if we decide to bake them into the agent in the future, -// they'll already conform. -// -// (The vast majority of this code was inspired by the database drivers implementation in the stdlib) - type rtfunc func(target string, args []string, timeout int) (map[string]string, error) type BaseTestlet struct { From 118c9b5e15f3cbb033ddf497fa1e281be4b719e9 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Thu, 22 Sep 2016 19:25:49 -0700 Subject: [PATCH 114/120] Removed duplicate code that checks for native testlets and retrieves path Signed-off-by: Matt Oswalt --- agent/tasks/installtestrun.go | 31 +++++++------------------------ agent/testing/testing.go | 34 +++++++++++++++++++++++++++++----- 2 files changed, 36 insertions(+), 29 deletions(-) diff --git a/agent/tasks/installtestrun.go b/agent/tasks/installtestrun.go index 9a783d4..683673f 100644 --- a/agent/tasks/installtestrun.go +++ b/agent/tasks/installtestrun.go @@ -11,8 +11,6 @@ package tasks import ( "bytes" "errors" - "fmt" - "os" "os/exec" "strings" @@ -43,30 +41,15 @@ func (itt InstallTestRunTask) Run() error { return errors.New("Testlet parameter for this testrun is null") } - var testlet_path string - // Determine if this is a native testlet - isNative, newName := testing.IsNativeTestlet(itt.Tr.Testlet) - - // If we're running a native testlet, we want testlet_path to simply be the testlet name - // (since it is a requirement that the native-Go testlets are in the PATH) - // If the testlet is not native, we can get the full path. - if isNative { - log.Infof("%s is a native testlet - installing testrun.", itt.Tr.Testlet) - testlet_path = newName - } else { - // Generate path to testlet and make sure it exists. - testlet_path = fmt.Sprintf("%s/assets/testlets/%s", itt.Config.LocalResources.OptDir, itt.Tr.Testlet) - - if _, err := os.Stat(testlet_path); os.IsNotExist(err) { - log.Errorf("Testlet %s does not exist on this agent", itt.Tr.Testlet) - return errors.New("Error installing testrun - testlet doesn't exist on this agent.") - } + testletPath, err := testing.GetTestletPath(itt.Tr.Testlet, itt.Config.LocalResources.OptDir) + if err != nil { + return err } // Run the testlet in check mode to verify that everything is okay to run this test - log.Debug("Running testlet in check mode: ", testlet_path) - cmd := exec.Command(testlet_path, "check") + log.Debug("Running testlet in check mode: ", testletPath) + cmd := exec.Command(testletPath, "check") // Stdout buffer cmdOutput := &bytes.Buffer{} @@ -77,7 +60,7 @@ func (itt InstallTestRunTask) Run() error { // This is probably the best cross-platform way to see if check mode passed. if strings.Contains(string(cmdOutput.Bytes()), "Check mode PASSED") { - log.Debugf("Check mode for %s passed", testlet_path) + log.Debugf("Check mode for %s passed", testletPath) } else { log.Error("Testlet returned an error during check mode: ", string(cmdOutput.Bytes())) return errors.New("Testlet returned an error during check mode") @@ -85,7 +68,7 @@ func (itt InstallTestRunTask) Run() error { // Insert testrun into agent cache var ac = cache.NewAgentCache(itt.Config) - err := ac.InsertTestRun(itt.Tr) + err = ac.InsertTestRun(itt.Tr) if err != nil { log.Error(err) return errors.New("Problem installing test run into agent cache") diff --git a/agent/testing/testing.go b/agent/testing/testing.go index 2485dda..001a162 100644 --- a/agent/testing/testing.go +++ b/agent/testing/testing.go @@ -11,6 +11,14 @@ package testing +import ( + "errors" + "fmt" + "os" + + log "github.com/Sirupsen/logrus" +) + var ( // This map provides name redirection so that the native testlets can use names that don't @@ -52,11 +60,27 @@ type BaseTestlet struct { } // IsNativeTestlet polls the list of registered native testlets, and returns -// true if the referenced name exists -func IsNativeTestlet(name string) (bool, string) { - if _, ok := nativeTestlets[name]; ok { - return true, nativeTestlets[name] +// true if the referenced name exists. Also returns path to testlet (if native, just it's name) +// If we're running a native testlet, we want testlet_path to simply be the testlet name +// (since it is a requirement that the native-Go testlets are in the PATH) +// If the testlet is not native, we can get the full path. +// << DOCUMENT RETURNS AND ARGS >> +func GetTestletPath(testletName, optDir string) (string, error) { + + if _, ok := nativeTestlets[testletName]; ok { + log.Infof("%s is a native testlet", testletName) + return nativeTestlets[testletName], nil } else { - return false, "" + + log.Infof("%s is a custom testlet", testletName) + + // Generate path to testlet and make sure it exists. + testletPath := fmt.Sprintf("%s/assets/testlets/%s", optDir, testletName) + if _, err := os.Stat(testletPath); os.IsNotExist(err) { + log.Errorf("Testlet %s does not exist on this agent", testletName) + return "", errors.New("Error installing testrun - testlet doesn't exist on this agent.") + } + + return testletPath, nil } } From 579b7b2bc2af2d7e5c64af1247ea09ee68b40609 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Thu, 22 Sep 2016 19:30:03 -0700 Subject: [PATCH 115/120] Updated docstring for GetTestletPath Signed-off-by: Matt Oswalt --- agent/testing/testing.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/agent/testing/testing.go b/agent/testing/testing.go index 001a162..9881dc7 100644 --- a/agent/testing/testing.go +++ b/agent/testing/testing.go @@ -59,12 +59,11 @@ type BaseTestlet struct { RunFunction rtfunc } -// IsNativeTestlet polls the list of registered native testlets, and returns -// true if the referenced name exists. Also returns path to testlet (if native, just it's name) -// If we're running a native testlet, we want testlet_path to simply be the testlet name -// (since it is a requirement that the native-Go testlets are in the PATH) -// If the testlet is not native, we can get the full path. -// << DOCUMENT RETURNS AND ARGS >> +// GetTestletPath generates whatever path is needed to reach the given testlet +// It first determines if the referenced testlet is native or not - if it is native, +// then only the name needs to be returned (all native testlets must be in the path). +// If it is a custom testlet, then it will generate the full path to the testlet, +// ensure it is a valid path, and if so, return that full path back to the caller. func GetTestletPath(testletName, optDir string) (string, error) { if _, ok := nativeTestlets[testletName]; ok { From 4b63c09dcbfcf6f65e27d7bee7f4afbc13569433 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Thu, 22 Sep 2016 19:35:33 -0700 Subject: [PATCH 116/120] Removed roadmap (will be re-added in upcoming docs revamp Signed-off-by: Matt Oswalt --- docs/roadmap.rst | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 docs/roadmap.rst diff --git a/docs/roadmap.rst b/docs/roadmap.rst deleted file mode 100644 index 9c9be29..0000000 --- a/docs/roadmap.rst +++ /dev/null @@ -1,17 +0,0 @@ -Roadmap -================================ - -Goals for Alpha Release - -* Store group-to-group aggreagate test data as a graph (agent groups are the lowest addressable node in teh graph) -* Consider getting rid of YAML files. It should be easy to run tests using a one-liner (native testlets should have reasonable defaults) or they just run on their own in the background -* Simplify agent setup. Is there a way to provide configuration-less agent setup? -* Need to look at modifying ToDD to run tests in a hands-off way. Think about the inspiration from the vendors at NFD12. Need to rethink the execution of tests. You know you can store test results without server connectivity, but can you run tests without server connectivity? - -Goals for Beta Release - -* Goal 1 - -Goals for Full Release - -* Web Front-End? \ No newline at end of file From 6776423aa4570496d65d69cb2ab4969e8e110f86 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Thu, 22 Sep 2016 19:36:07 -0700 Subject: [PATCH 117/120] Removed old DSL files Signed-off-by: Matt Oswalt --- docs/dsl/branch-to-dc-bw.yml | 14 -------------- docs/dsl/group-branch.yml | 7 ------- docs/dsl/group-uraj.yml | 7 ------- 3 files changed, 28 deletions(-) delete mode 100644 docs/dsl/branch-to-dc-bw.yml delete mode 100644 docs/dsl/group-branch.yml delete mode 100644 docs/dsl/group-uraj.yml diff --git a/docs/dsl/branch-to-dc-bw.yml b/docs/dsl/branch-to-dc-bw.yml deleted file mode 100644 index b1683a4..0000000 --- a/docs/dsl/branch-to-dc-bw.yml +++ /dev/null @@ -1,14 +0,0 @@ ---- -# Example test file -type: testrun -label: branch-to-dc-bw -spec: - targettype: group - source: - name: branch-uraj - app: iperf - args: "-c {{ target }}" - target: - name: datacenter - app: iperf - args: "-s" diff --git a/docs/dsl/group-branch.yml b/docs/dsl/group-branch.yml deleted file mode 100644 index b1d1e82..0000000 --- a/docs/dsl/group-branch.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -type: group -label: branch-uraj -spec: - group: branch-uraj - matches: - - hostname: "uraj" diff --git a/docs/dsl/group-uraj.yml b/docs/dsl/group-uraj.yml deleted file mode 100644 index b1d1e82..0000000 --- a/docs/dsl/group-uraj.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -type: group -label: branch-uraj -spec: - group: branch-uraj - matches: - - hostname: "uraj" From 4905176f0b8f38dc8167ace7885d27f069f89d71 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Thu, 22 Sep 2016 19:36:25 -0700 Subject: [PATCH 118/120] Used correct loglevel in GetTestletPath Signed-off-by: Matt Oswalt --- agent/testing/testing.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agent/testing/testing.go b/agent/testing/testing.go index 9881dc7..539ad21 100644 --- a/agent/testing/testing.go +++ b/agent/testing/testing.go @@ -67,11 +67,11 @@ type BaseTestlet struct { func GetTestletPath(testletName, optDir string) (string, error) { if _, ok := nativeTestlets[testletName]; ok { - log.Infof("%s is a native testlet", testletName) + log.Debugf("%s is a native testlet", testletName) return nativeTestlets[testletName], nil } else { - log.Infof("%s is a custom testlet", testletName) + log.Debugf("%s is a custom testlet", testletName) // Generate path to testlet and make sure it exists. testletPath := fmt.Sprintf("%s/assets/testlets/%s", optDir, testletName) From 0b13b925b76c274350edb8adf737ea266fc5b702 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Sat, 24 Sep 2016 09:01:24 -0700 Subject: [PATCH 119/120] Incorporated changes suggested in review Signed-off-by: Matt Oswalt --- agent/testing/testing.go | 40 +++++++++++----------------------------- cmd/todd-agent/main.go | 2 +- 2 files changed, 12 insertions(+), 30 deletions(-) diff --git a/agent/testing/testing.go b/agent/testing/testing.go index 539ad21..b759df8 100644 --- a/agent/testing/testing.go +++ b/agent/testing/testing.go @@ -38,25 +38,7 @@ type Testlet interface { // Run is the "workflow" function for a testlet. All testing takes place here // (or in a function called within) - // - // Params are - // target (string) - // args ([]string) - // timeLimit (int in seconds) - // - // Returns: - // metrics (map[string]string) - // (name of metric is key, value is metric value) - Run(string, []string, int) (map[string]string, error) -} - -type rtfunc func(target string, args []string, timeout int) (map[string]string, error) - -type BaseTestlet struct { - - // rtfunc is a type that will store our RunTestlet function. It is the responsibility - // of the "child" testlet to set this value upon creation - RunFunction rtfunc + Run(target string, args []string, timeLimit int) (metrics map[string]string, err error) } // GetTestletPath generates whatever path is needed to reach the given testlet @@ -69,17 +51,17 @@ func GetTestletPath(testletName, optDir string) (string, error) { if _, ok := nativeTestlets[testletName]; ok { log.Debugf("%s is a native testlet", testletName) return nativeTestlets[testletName], nil - } else { - - log.Debugf("%s is a custom testlet", testletName) + } - // Generate path to testlet and make sure it exists. - testletPath := fmt.Sprintf("%s/assets/testlets/%s", optDir, testletName) - if _, err := os.Stat(testletPath); os.IsNotExist(err) { - log.Errorf("Testlet %s does not exist on this agent", testletName) - return "", errors.New("Error installing testrun - testlet doesn't exist on this agent.") - } + log.Debugf("%s is a custom testlet", testletName) - return testletPath, nil + // Generate path to testlet and make sure it exists. + testletPath := fmt.Sprintf("%s/assets/testlets/%s", optDir, testletName) + if _, err := os.Stat(testletPath); err != nil || os.IsNotExist(err) { + log.Errorf("Testlet %q does not exist on this agent", testletName) + return "", errors.New("Error installing testrun - testlet doesn't exist on this agent.") } + + return testletPath, nil + } diff --git a/cmd/todd-agent/main.go b/cmd/todd-agent/main.go index aa39c81..dfecfc0 100644 --- a/cmd/todd-agent/main.go +++ b/cmd/todd-agent/main.go @@ -146,7 +146,7 @@ func watchForFinishedTestRuns(cfg config.Config) error { log.Debug("Found ripe testrun: ", testUuid) - var utdr = responses.UploadTestDataResponse{ + utdr := responses.UploadTestDataResponse{ TestUuid: testUuid, TestData: testData, } From 6f1db2492fdf129dd462087678363bff5be6697a Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Sun, 25 Sep 2016 20:43:24 -0400 Subject: [PATCH 120/120] Made error handling for testlet not existing more generic Signed-off-by: Matt Oswalt --- agent/testing/testing.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/agent/testing/testing.go b/agent/testing/testing.go index b759df8..5845341 100644 --- a/agent/testing/testing.go +++ b/agent/testing/testing.go @@ -57,9 +57,9 @@ func GetTestletPath(testletName, optDir string) (string, error) { // Generate path to testlet and make sure it exists. testletPath := fmt.Sprintf("%s/assets/testlets/%s", optDir, testletName) - if _, err := os.Stat(testletPath); err != nil || os.IsNotExist(err) { - log.Errorf("Testlet %q does not exist on this agent", testletName) - return "", errors.New("Error installing testrun - testlet doesn't exist on this agent.") + if _, err := os.Stat(testletPath); err != nil { + log.Errorf("Problem accessing testlet %q on this agent", testletName) + return "", errors.New("Error installing testrun - problem accessing testrun on agent") } return testletPath, nil