From ebf0001c5442edb3cbe30ef80fca33a358b5dbcb Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Tue, 19 Jul 2016 01:35:36 -0700 Subject: [PATCH 01/13] 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 02/13] 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 03/13] 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 04/13] 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 05/13] 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 06/13] 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 07/13] 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 08/13] 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 09/13] 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 10/13] 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 11/13] 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 ba44cbce07a33dab72537e1299b02f9447d4a827 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Mon, 22 Aug 2016 23:41:17 -0700 Subject: [PATCH 12/13] Re-vamp of docs Signed-off-by: Matt Oswalt --- docs/{ => dependencies}/comms.rst | 4 +- docs/dependencies/db.rst | 4 ++ docs/dependencies/dependencies.rst | 25 ++++++++++ docs/dependencies/tsdb.rst | 4 ++ docs/design.rst | 28 +++++++++-- docs/index.rst | 20 +++----- docs/installconfig/compile.rst | 35 +++++++++++++ docs/{ => installconfig}/configure.rst | 0 .../containerorvm.rst} | 40 +-------------- docs/installconfig/install.rst | 12 +++++ docs/{ => installconfig}/installrpi.rst | 2 +- docs/intro.rst | 20 ++++++++ docs/resources.rst | 6 ++- docs/testlets.rst | 39 --------------- docs/{ => testlets}/customtestlets.rst | 2 +- docs/testlets/testlets.rst | 49 +++++++++++++++++++ docs/{ => using}/cli.rst | 0 docs/{ => using}/objects.rst | 0 docs/using/using.rst | 10 ++++ 19 files changed, 199 insertions(+), 101 deletions(-) rename docs/{ => dependencies}/comms.rst (97%) create mode 100644 docs/dependencies/db.rst create mode 100644 docs/dependencies/dependencies.rst create mode 100644 docs/dependencies/tsdb.rst create mode 100644 docs/installconfig/compile.rst rename docs/{ => installconfig}/configure.rst (100%) rename docs/{install.rst => installconfig/containerorvm.rst} (53%) create mode 100644 docs/installconfig/install.rst rename docs/{ => installconfig}/installrpi.rst (99%) create mode 100644 docs/intro.rst delete mode 100644 docs/testlets.rst rename docs/{ => testlets}/customtestlets.rst (99%) create mode 100644 docs/testlets/testlets.rst rename docs/{ => using}/cli.rst (100%) rename docs/{ => using}/objects.rst (100%) create mode 100644 docs/using/using.rst diff --git a/docs/comms.rst b/docs/dependencies/comms.rst similarity index 97% rename from docs/comms.rst rename to docs/dependencies/comms.rst index 3bd306b..4a88019 100644 --- a/docs/comms.rst +++ b/docs/dependencies/comms.rst @@ -1,9 +1,9 @@ -ToDD Agent Orchestration (Comms) +Agent Communication (Comms) ================================ In ToDD, we use a simple abstraction for communicating between the ToDD server and the agents, and this is implemented in the "comms" package. This abstraction allows ToDD to easily work with external messaging services like RabbitMQ, or Apache Kafka, etc - in order to facilitate distributed communications. As a result, ToDD agents do not actually speak directly with the ToDD server - they communicate indirectly through this third-party entitity. -.. image:: images/comms.png +.. image:: ../images/comms.png As shown in `#18 `_, this paradigm wasn't very well documented until now. ToDD still requires that some external entity like RabbitMQ is set up. diff --git a/docs/dependencies/db.rst b/docs/dependencies/db.rst new file mode 100644 index 0000000..216ac39 --- /dev/null +++ b/docs/dependencies/db.rst @@ -0,0 +1,4 @@ +State Database +================================ + +database \ No newline at end of file diff --git a/docs/dependencies/dependencies.rst b/docs/dependencies/dependencies.rst new file mode 100644 index 0000000..e4e360f --- /dev/null +++ b/docs/dependencies/dependencies.rst @@ -0,0 +1,25 @@ +ToDD Dependencies +================================ + +.. toctree:: + :maxdepth: 2 + + comms.rst + db.rst + tsdb.rst + +Internal Dependencies +--------------------- + +If you're not a developer concerned with the internals of ToDD, this is not something you need to worry about. Internal dependencies, such as Go libraries that ToDD uses to communicate with a message queue, or a database for example, are vendored in the `vendor` directory of the repository. + +External Dependencies +--------------------- + +There are a number of external services that ToDD needs + +- `Agent Communications `_ +- `State Database `_ +- `Time-Series Database `_ + +Each of these dependencies can potentially be satisfied by multiple existing software projects, but please refer to the specific pages for each to see what specific integrations have been built into ToDD thus far. \ No newline at end of file diff --git a/docs/dependencies/tsdb.rst b/docs/dependencies/tsdb.rst new file mode 100644 index 0000000..4525cdd --- /dev/null +++ b/docs/dependencies/tsdb.rst @@ -0,0 +1,4 @@ +Time-Series Database +================================ + +database \ No newline at end of file diff --git a/docs/design.rst b/docs/design.rst index 8de2ca9..c5a7058 100644 --- a/docs/design.rst +++ b/docs/design.rst @@ -1,20 +1,29 @@ ToDD - High-Level Design ================================ -The High-Level Design of ToDD is fairly simple: + +The High-Level Design of ToDD is fairly simple. ToDD is composed of three components: + +* Server +* Agent +* Client + +A general idea of how these components is depicted below: .. image:: images/todd-hld.png Some notes about this: -* All database integrations are at the server level -* Agents communicate with server via "comms" abstraction (currently RMQ is supported) -* Server provides REST API to either the pre-packaged "todd" client tool, or to 3rd party software +* All database integrations are at the server level - no agent communicates with database +* Agents _do not_ communicate directly with server. This is done through some kind of message queue (i.e. RabbitMQ) using ToDD's "comms" abstraction. +* Server has a REST API built-in. No other software needed (see section "ToDD Server" for more) + +The following sections elaborate on each component in greater detail. ToDD Server ----------- -The ToDD Server has a few particular noteworth attributes: +The ToDD Server has a few particular noteworthy attributes: * Orchestrates test runs between groups of agents (not an endpoint for any testing) * Manages agent registration and remediation @@ -32,3 +41,12 @@ Here are some specific notes on the agents. * Provides facts about operating environment back to server * Receives and executes testrun instructions from server * Variety of form factors (baremetal, VM, container, RasPi, network switch) + +ToDD Client +----------- + +The ToDD client is provided via the "todd" shell command. Several subcommands are available here, such as "todd agents" to print the list of currently registered agents. + +* Manages installed ToDD objects (group and testrun definitions, etc) +* Queries state of ToDD infrastructure ("todd agents", "todd groups", etc.) +* Executes testruns ("todd run ...") \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst index e117dca..109f57a 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -7,24 +7,20 @@ ToDD Documentation ================================ .. toctree:: - :maxdepth: 2 + :maxdepth: 3 - cli.rst - comms.rst - configure.rst + intro.rst design.rst - install.rst - installrpi.rst - objects.rst + installconfig/install.rst + dependencies/dependencies.rst + using/using.rst + testlets/testlets.rst resources.rst - testlets.rst - customtestlets.rst + roadmap.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. In the meantime, check out my `blog post on ToDD `_, as it's probably the best introduction to using ToDD for the time being. -The mailing list for ToDD is available here: `https://groups.google.com/forum/#!forum/todd-dev `_ - -TODO: Put in high level design and make sure to mention dependencies like rabbitmq, etcd, etc. (in that they're not hard dependencies but currently they are the only implementations) +The mailing list for ToDD is `available here `_. diff --git a/docs/installconfig/compile.rst b/docs/installconfig/compile.rst new file mode 100644 index 0000000..6bb8603 --- /dev/null +++ b/docs/installconfig/compile.rst @@ -0,0 +1,35 @@ +Compile from Source +================================ + +First, make sure the following software is installed and correctly configured for your platform: + +- Go (1.6 is the version tested for this documentation) +- Make sure the "bin" directory in your GOPATH is also added to your "PATH" +- Git + +.. topic:: NOTE + + If you are installing ToDD on a Raspberry Pi, there are specialized install instructions `here `_. + +The best way to install ToDD onto a system is with the provided Makefile. In this section, we'll retrieve the ToDD source, compile into the three ToDD binaries, and install these binaries onto the system. + +First, let's ``go get`` the ToDD source. As mentioned at the beginning of this document, this assumes a system where Go has been properly set up: + +.. code-block:: text + + go get -d github.com/Mierdin/todd + +At this point, you may get an error along the lines of "no buildable GO source files in...". Ignore this error; you should still be able to install ToDD. + +Navigate to the directory where Go would have downloaded ToDD. As an example: + +.. code-block:: text + + cd $GOPATH/src/github.com/Mierdin/todd + +Finally, compile and install the binaries: + +.. code-block:: text + + make + sudo make install diff --git a/docs/configure.rst b/docs/installconfig/configure.rst similarity index 100% rename from docs/configure.rst rename to docs/installconfig/configure.rst diff --git a/docs/install.rst b/docs/installconfig/containerorvm.rst similarity index 53% rename from docs/install.rst rename to docs/installconfig/containerorvm.rst index 78741e7..0274a69 100644 --- a/docs/install.rst +++ b/docs/installconfig/containerorvm.rst @@ -1,43 +1,6 @@ -Installing and/or Running ToDD +Using ToDD in a Container or Virtual Machine ================================ -First, make sure the following software is installed and correctly configured for your platform: - -- Go (1.6 is the version tested for this documentation) -- Make sure the "bin" directory in your GOPATH is also added to your "PATH" -- Git - -.. topic:: NOTE - - If you are installing ToDD on a Raspberry Pi, there are specialized install instructions `here `_. - - -Install via Make ----------------- - -The best way to install ToDD onto a system is with the provided Makefile. In this section, we'll retrieve the ToDD source, compile into the three ToDD binaries, and install these binaries onto the system. - -First, let's ``go get`` the ToDD source. As mentioned at the beginning of this document, this assumes a system where Go has been properly set up: - -.. code-block:: text - - go get -d github.com/Mierdin/todd - -At this point, you may get an error along the lines of "no buildable GO source files in...". Ignore this error; you should still be able to install ToDD. - -Navigate to the directory where Go would have downloaded ToDD. As an example: - -.. code-block:: text - - cd $GOPATH/src/github.com/Mierdin/todd - -Finally, compile and install the binaries: - -.. code-block:: text - - make - sudo make install - Docker ------ If you instead wish to run ToDD inside a Docker container, you can pull the current image from Dockerhub: @@ -71,4 +34,3 @@ There is also a provided vagrantfile in the repo. This is not something you shou .. code-block:: text vagrant up - diff --git a/docs/installconfig/install.rst b/docs/installconfig/install.rst new file mode 100644 index 0000000..75b23ec --- /dev/null +++ b/docs/installconfig/install.rst @@ -0,0 +1,12 @@ +Installing and/or Running ToDD +================================ + +.. toctree:: + :maxdepth: 2 + + compile.rst + containerorvm.rst + installrpi.rst + configure.rst + +These instructions will help you get ToDD running on your system. However, keep in mind that once you've done this, there are also some `external dependencies <../dependencies/dependencies.html>`_ that must also be provided. diff --git a/docs/installrpi.rst b/docs/installconfig/installrpi.rst similarity index 99% rename from docs/installrpi.rst rename to docs/installconfig/installrpi.rst index d87e7ab..e300e9d 100644 --- a/docs/installrpi.rst +++ b/docs/installconfig/installrpi.rst @@ -1,7 +1,7 @@ Installing on Raspberry Pi ================================ -.. image:: images/rpi.jpeg +.. image:: ../images/rpi.jpeg .. IMPORTANT:: These instructions were tested on several Raspberry Pi 3 (Model B). In theory, these instructions should work for other models, but may require some minor modifications. diff --git a/docs/intro.rst b/docs/intro.rst new file mode 100644 index 0000000..65d1f58 --- /dev/null +++ b/docs/intro.rst @@ -0,0 +1,20 @@ +Introduction to ToDD +================================ + +Welcome to the documentation for ToDD! ToDD is an extensible framework for providing natively distributed network testing on demand. ToDD is an acronym that stands for "Testing on Demand: Distributed!". + +Traditionally, the tooling used by network engineers to confirm continued network operation after any kind of change to the network is fairly limited. After a change, a network engineer may run "ping" or "traceroute" from their machine, or perhaps call some application owners to ensure that their apps are still working. Unfortunately, there is a very big difference in network activity between a 3AM change window and peak user activity during the day. + +What's needed is a way to: + +* Describe a specific application that uses the network, in a simple text format +* Artificially produce network traffic that matches this description, at a comparable scale to real-world network activity + +ToDD is a framework through which you can deploy simple test-oriented applications in a distributed manner. With ToDD, you distribute agents around your infrastructure in any place where you feel additional "testing power" is warranted. Then, these agents can be leveraged to mimic real-world network utilization by actually running those same applications at a large scale. + +Here are some key features provided by ToDD: + +- **Highly Extensible** - ToDD uses an extremely generic interface (called testlets) for running applications, so that users can very easily augment ToDD to support a new application. +- **Post-Test Analytics** - ToDD integrates with time-series databases, such as influxdb. With this, engineers can schedule ToDD test runs to occur periodically, and observe the testrun metrics changing over time. +- **Grouping** - ToDD performs testruns from groups of agents, instead of one specific agent. The user will provide a set of rules that place a given agent into a group (such as hostname, or ip subnet), and ToDD will instruct all agents in that group to perform the test. This means that the power of a test can be increased by simply spinning up additional agents in that group. +- **Diverse Target Types** - Test runs can be configured to target a list of "dumb" targets (targets that are not running a ToDD agent), or a ToDD group. This is useful for certain applications where you need to be able to set up both ends of a test (i.e. setting up a webserver and then testing against it with curl, or setting up an iperf client/server combo) \ No newline at end of file diff --git a/docs/resources.rst b/docs/resources.rst index c53b15b..00916b7 100644 --- a/docs/resources.rst +++ b/docs/resources.rst @@ -3,7 +3,9 @@ Additional Resources for ToDD Collaboration ---------- -ToDD has a `mailing list `_, for all matters related to development, or questions about getting ToDD running on your own systems. Please feel free to join and send a message if you have any questions! We will be as helpful as possible. +The most active platform at the moment is the #todd channel in the "Network to Code" slack team. This team is free and open to join - just head over to `networktocode.slack.com `_ to sign up, and look for the #todd channel once in. + +ToDD also has a `mailing list `_, for all matters related to development, or questions about getting ToDD running on your own systems. Please feel free to join and send a message if you have any questions! We will be as helpful as possible. Blogs ---------- @@ -16,7 +18,7 @@ ToDD was first released at the Devops Networking Forum in Santa Clara. That talk .. raw:: html -
+ I also recorded a video that covers some of the high-level concepts and design of ToDD: diff --git a/docs/testlets.rst b/docs/testlets.rst deleted file mode 100644 index 092742d..0000000 --- a/docs/testlets.rst +++ /dev/null @@ -1,39 +0,0 @@ -Testlets -================================ - -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. - -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 -* 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. - - -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. - -* 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? - -* 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/docs/customtestlets.rst b/docs/testlets/customtestlets.rst similarity index 99% rename from docs/customtestlets.rst rename to docs/testlets/customtestlets.rst index a7023a0..efec667 100644 --- a/docs/customtestlets.rst +++ b/docs/testlets/customtestlets.rst @@ -5,7 +5,7 @@ ToDD was originally built with no testlets built-in to the agent. All tests were 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 +.. 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 diff --git a/docs/testlets/testlets.rst b/docs/testlets/testlets.rst new file mode 100644 index 0000000..3324578 --- /dev/null +++ b/docs/testlets/testlets.rst @@ -0,0 +1,49 @@ +Testlets +================================ + +.. toctree:: + :maxdepth: 2 + + customtestlets.rst + +Testing applications are called "testlets" in ToDD. This is a handy way of referring to "whatever is actually doing the work of testing". This concept keeps things very neatly separated - the testlets focus on performing tests, and ToDD focuses on ensuring that work is distributed as the user directs. + +There are a number of testlets that have been developed as part of the ToDD project (referred to as "native testlets"): + +* `http `_ +* `bandwidth `_ +* `ping `_ +* `portknock `_ + +They run as separate binaries, and are executed in the same way that custom testlets might be executed, if you were to provide one. If you install ToDD using the provided instructions, these are also installed on the system. + +.. NOTE:: + + If, however, you wish to build your own custom testlets, refer to `Custom Testlets `_; 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. + +If you're not a developer, and/or you just want to USE these native testlets, you can install these binaries anywhere in your PATH. The quickstart instructions illustrate how to unzip the testlets into the right directory. (TODO make sure this is the case) + +Developing Native Testlets for ToDD +------------------------------------- + +Native Testlets are maintained in their own separate repositories but are distributed alongside ToDD itself. They are also 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) + +The native testlets must be installed somewhere that your PATH environment variable knows about. The typical way to ensure this during development is to just use the Makefile, which kicks off some scripts that perform "go get" commands for the native testlet repositories, and if your GOPATH is set up correctly, the binaries are placed in $GOPATH/bin. Of course, $GOPATH/bin must also be in your PATH variable, which is also a best practice for any Go project. + +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? + +* 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/docs/cli.rst b/docs/using/cli.rst similarity index 100% rename from docs/cli.rst rename to docs/using/cli.rst diff --git a/docs/objects.rst b/docs/using/objects.rst similarity index 100% rename from docs/objects.rst rename to docs/using/objects.rst diff --git a/docs/using/using.rst b/docs/using/using.rst new file mode 100644 index 0000000..52b3a7f --- /dev/null +++ b/docs/using/using.rst @@ -0,0 +1,10 @@ +Using ToDD +================================ + +.. toctree:: + :maxdepth: 2 + + cli.rst + objects.rst + +This guide will show you how to use ToDD \ No newline at end of file From e2114dfb1c056a328130c420a59824a1741220c2 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Tue, 13 Sep 2016 00:41:36 -0700 Subject: [PATCH 13/13] Removed goals Signed-off-by: Matt Oswalt --- docs/roadmap.rst | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/docs/roadmap.rst b/docs/roadmap.rst index 9c9be29..d65dee4 100644 --- a/docs/roadmap.rst +++ b/docs/roadmap.rst @@ -3,10 +3,7 @@ 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? +* Goal 1 Goals for Beta Release