diff --git a/zititest/models/simple/actions/bootstrap.go b/zititest/models/simple/actions/bootstrap.go index a5f2c5a89..ade5011da 100644 --- a/zititest/models/simple/actions/bootstrap.go +++ b/zititest/models/simple/actions/bootstrap.go @@ -17,6 +17,7 @@ package actions import ( + "fmt" "github.com/openziti/ziti/zititest/zitilab" "time" @@ -68,56 +69,52 @@ func (a *bootstrapAction) bind(m *model.Model) model.Action { "protocol" : "tcp" }`)) - workflow.AddAction(zitilib_actions.Edge("create", "config", "files-intercept-ert-unencrypted", "intercept.v1", ` + workflow.AddAction(zitilib_actions.Edge("create", "config", "iperf-host", "host.v1", ` { - "addresses": ["ziti-files-ert-unencrypted.s3-us-west-1.amazonaws.ziti"], - "portRanges" : [ { "low": 443, "high": 443 } ], - "protocols": ["tcp"] - }`)) - - workflow.AddAction(zitilib_actions.Edge("create", "config", "files-intercept-ert", "intercept.v1", ` - { - "addresses": ["ziti-files-ert.s3-us-west-1.amazonaws.ziti"], - "portRanges" : [ { "low": 443, "high": 443 } ], - "protocols": ["tcp"] - }`)) - - workflow.AddAction(zitilib_actions.Edge("create", "config", "files-intercept-zet-unencrypted", "intercept.v1", ` - { - "addresses": ["ziti-files-zet-unencrypted.s3-us-west-1.amazonaws.ziti"], - "portRanges" : [ { "low": 443, "high": 443 } ], - "protocols": ["tcp"] - }`)) - - workflow.AddAction(zitilib_actions.Edge("create", "config", "files-intercept-zet", "intercept.v1", ` - { - "addresses": ["ziti-files-zet.s3-us-west-1.amazonaws.ziti"], - "portRanges" : [ { "low": 443, "high": 443 } ], - "protocols": ["tcp"] - }`)) - - workflow.AddAction(zitilib_actions.Edge("create", "config", "files-intercept-ziti-tunnel-unencrypted", "intercept.v1", ` - { - "addresses": ["ziti-files-ziti-tunnel-unencrypted.s3-us-west-1.amazonaws.ziti"], - "portRanges" : [ { "low": 443, "high": 443 } ], - "protocols": ["tcp"] - }`)) - - workflow.AddAction(zitilib_actions.Edge("create", "config", "files-intercept-ziti-tunnel", "intercept.v1", ` - { - "addresses": ["ziti-files-ziti-tunnel.s3-us-west-1.amazonaws.ziti"], - "portRanges" : [ { "low": 443, "high": 443 } ], - "protocols": ["tcp"] + "address" : "localhost", + "port" : 5201, + "protocol" : "tcp" }`)) - workflow.AddAction(zitilib_actions.Edge("create", "service", "ert-files-unencrypted", "-c", "files-host,files-intercept-ert-unencrypted", "-e", "OFF", "-a", "ert")) - workflow.AddAction(zitilib_actions.Edge("create", "service", "ert-files", "-c", "files-host,files-intercept-ert", "-a", "ert")) - - workflow.AddAction(zitilib_actions.Edge("create", "service", "zet-files-unencrypted", "-c", "files-host,files-intercept-zet-unencrypted", "-e", "OFF", "-a", "zet")) - workflow.AddAction(zitilib_actions.Edge("create", "service", "zet-files", "-c", "files-host,files-intercept-zet", "-a", "zet")) - - workflow.AddAction(zitilib_actions.Edge("create", "service", "ziti-tunnel-files-unencrypted", "-c", "files-host,files-intercept-ziti-tunnel-unencrypted", "-e", "OFF", "-a", "ziti-tunnel")) - workflow.AddAction(zitilib_actions.Edge("create", "service", "ziti-tunnel-files", "-c", "files-host,files-intercept-ziti-tunnel", "-a", "ziti-tunnel")) + for _, encrypted := range []bool{false, true} { + for _, hostType := range []string{"ert", "zet", "ziti-tunnel"} { + suffix := "" + encryptionFlag := "ON" + + if !encrypted { + suffix = "-unencrypted" + encryptionFlag = "OFF" + } + + filesConfigName := fmt.Sprintf("files-intercept-%s%s", hostType, suffix) + filesConfigDef := fmt.Sprintf(` + { + "addresses": ["files-%s%s.s3-us-west-1.amazonaws.ziti"], + "portRanges" : [ { "low": 443, "high": 443 } ], + "protocols": ["tcp"] + }`, hostType, suffix) + + workflow.AddAction(zitilib_actions.Edge("create", "config", filesConfigName, "intercept.v1", filesConfigDef)) + + iperfConfigName := fmt.Sprintf("iperf-intercept-%s%s", hostType, suffix) + iperfConfigDef := fmt.Sprintf(` + { + "addresses": ["iperf-%s%s.ziti"], + "portRanges" : [ { "low": 5201, "high": 5201 } ], + "protocols": ["tcp"] + }`, hostType, suffix) + + workflow.AddAction(zitilib_actions.Edge("create", "config", iperfConfigName, "intercept.v1", iperfConfigDef)) + + filesServiceName := fmt.Sprintf("%s-files%s", hostType, suffix) + filesConfigs := fmt.Sprintf("files-host,%s", filesConfigName) + workflow.AddAction(zitilib_actions.Edge("create", "service", filesServiceName, "-c", filesConfigs, "-e", encryptionFlag, "-a", hostType)) + + iperfServiceName := fmt.Sprintf("%s-iperf%s", hostType, suffix) + iperfConfigs := fmt.Sprintf("iperf-host,%s", iperfConfigName) + workflow.AddAction(zitilib_actions.Edge("create", "service", iperfServiceName, "-c", iperfConfigs, "-e", encryptionFlag, "-a", hostType)) + } + } workflow.AddAction(zitilib_actions.Edge("create", "service-policy", "ert-hosts", "Bind", "--service-roles", "#ert", "--identity-roles", "#ert-host")) workflow.AddAction(zitilib_actions.Edge("create", "service-policy", "zet-hosts", "Bind", "--service-roles", "#zet", "--identity-roles", "#zet-host")) diff --git a/zititest/models/simple/actions/start.go b/zititest/models/simple/actions/start.go index afbed82af..05c50e1f8 100644 --- a/zititest/models/simple/actions/start.go +++ b/zititest/models/simple/actions/start.go @@ -41,6 +41,7 @@ func (a *startAction) bind(m *model.Model) model.Action { workflow.AddAction(component.Start("#ctrl")) workflow.AddAction(edge.ControllerAvailable("#ctrl", 30*time.Second)) workflow.AddAction(component.StartInParallel(models.EdgeRouterTag, 25)) + workflow.AddAction(component.StartInParallel(".iperf", 5)) workflow.AddAction(semaphore.Sleep(2 * time.Second)) workflow.AddAction(zitilib_actions.StartMetricbeat("*", a.Metricbeat.ConfigPath, a.Metricbeat.DataPath, a.Metricbeat.LogPath)) diff --git a/zititest/models/simple/simple.go b/zititest/models/simple/simple.go index 651566449..503b2402b 100644 --- a/zititest/models/simple/simple.go +++ b/zititest/models/simple/simple.go @@ -164,6 +164,10 @@ var Model = &model.Model{ Scope: model.Scope{Tags: model.Tags{"sdk-app", "service"}}, Type: &zitilab.EchoServerType{}, }, + "iperf-server-ert": { + Scope: model.Scope{Tags: model.Tags{"iperf", "service"}}, + Type: &zitilab.IPerfServerType{}, + }, }, }, "ziti-edge-tunnel-host": { @@ -174,6 +178,10 @@ var Model = &model.Model{ Version: "v0.21.4", }, }, + "iperf-server-zet": { + Scope: model.Scope{Tags: model.Tags{"iperf", "service"}}, + Type: &zitilab.IPerfServerType{}, + }, }, }, "ziti-tunnel-host": { @@ -184,6 +192,10 @@ var Model = &model.Model{ Mode: zitilab.ZitiTunnelModeHost, }, }, + "iperf-server-zt": { + Scope: model.Scope{Tags: model.Tags{"iperf", "service"}}, + Type: &zitilab.IPerfServerType{}, + }, }, }, }, diff --git a/zititest/report.xml b/zititest/report.xml new file mode 100644 index 000000000..e69de29bb diff --git a/zititest/tests/files_test.go b/zititest/tests/files_test.go index 5e4d5de14..562025bd4 100644 --- a/zititest/tests/files_test.go +++ b/zititest/tests/files_test.go @@ -32,8 +32,8 @@ var hashes = map[string]string{ } var timeouts = map[string]time.Duration{ - "1KB": 5 * time.Second, - "100KB": 5 * time.Second, + "1KB": 10 * time.Second, + "100KB": 10 * time.Second, "20MB": 40 * time.Second, } @@ -44,38 +44,69 @@ const ( ClientWget httpClient = "wget" ) -func TestCurlFiles(t *testing.T) { +func TestDownloadFiles(t *testing.T) { allZetHostedFailed := true - for _, hostType := range []string{"ert", "zet", "ziti-tunnel"} { - for _, clientType := range []string{"ert", "zet", "ziti-tunnel"} { - for _, encrypted := range []bool{true, false} { - success := testFileDownload(t, clientType, ClientCurl, hostType, encrypted, "1KB") - if hostType == "zet" && success { - allZetHostedFailed = false + allZetClientsFailed := true + + t.Run("download-tests", func(t *testing.T) { + t.Run("test-ert-downloads", func(t *testing.T) { + t.Parallel() + + for _, size := range []string{"1KB", "100KB", "20MB"} { + for _, hostType := range []string{"ert", "zet", "ziti-tunnel"} { + for _, client := range []httpClient{ClientCurl, ClientWget} { + for _, encrypted := range []bool{true, false} { + success := testFileDownload(t, "ert", client, hostType, encrypted, size) + if hostType == "zet" && success { + allZetHostedFailed = false + } + } + } } } - } - } - - for _, size := range []string{"100KB", "20MB"} { - for _, clientType := range []string{"ert", "zet"} { - for _, hostType := range []string{"ert", "zet"} { - for _, client := range []httpClient{ClientCurl, ClientWget} { - for _, encrypted := range []bool{true, false} { - success := testFileDownload(t, clientType, client, hostType, encrypted, size) - if hostType == "zet" && success { - allZetHostedFailed = false + }) + + t.Run("test-zet-downloads", func(t *testing.T) { + t.Parallel() + + for _, size := range []string{"1KB", "100KB", "20MB"} { + for _, hostType := range []string{"zet", "ziti-tunnel", "ert"} { + for _, client := range []httpClient{ClientCurl, ClientWget} { + for _, encrypted := range []bool{true, false} { + success := testFileDownload(t, "zet", client, hostType, encrypted, size) + if hostType == "zet" && success { + allZetHostedFailed = false + } + if success { + allZetClientsFailed = false + } } } } } - } - } - - testFileDownload(t, "ziti-tunnel", ClientCurl, "ziti-tunnel", true, "20MB") + }) + + t.Run("test-ziti-tunnel-downloads", func(t *testing.T) { + t.Parallel() + + for _, size := range []string{"1KB", "100KB", "20MB"} { + for _, hostType := range []string{"ziti-tunnel", "ert", "zet"} { + for _, client := range []httpClient{ClientCurl, ClientWget} { + for _, encrypted := range []bool{true, false} { + success := testFileDownload(t, "ziti-tunnel", client, hostType, encrypted, size) + if hostType == "zet" && success { + allZetHostedFailed = false + } + } + } + } + } + }) + }) req := require.New(t) req.False(allZetHostedFailed, "all zet hosted file transfer should not failed, indicates bigger issue") + req.False(allZetClientsFailed, "all zet client file transfers should not failed, indicates bigger issue") } func testFileDownload(t *testing.T, hostSelector string, client httpClient, hostType string, encrypted bool, fileSize string) bool { @@ -96,7 +127,7 @@ func testFileDownload(t *testing.T, hostSelector string, client httpClient, host urlExtra = "-unencrypted" } - url := fmt.Sprintf("https://ziti-files-%s%s.s3-us-west-1.amazonaws.ziti/%s.zip", hostType, urlExtra, fileSize) + url := fmt.Sprintf("https://files-%s%s.s3-us-west-1.amazonaws.ziti/%s.zip", hostType, urlExtra, fileSize) sshConfigFactory := lib.NewSshConfigFactory(host) var cmd string @@ -112,6 +143,13 @@ func testFileDownload(t *testing.T, hostSelector string, client httpClient, host t.Skipf("zet hosted file transfer failed [%v]", err.Error()) return } + + if hostSelector == "zet" && err != nil { + t.Skipf("zet client file transfer failed [%v]", err.Error()) + return + } + + t.Log(o) req.NoError(err) req.Equal(hashes[fileSize], o[0:32]) success = true diff --git a/zititest/tests/iperf_test.go b/zititest/tests/iperf_test.go new file mode 100644 index 000000000..ed3cb1dff --- /dev/null +++ b/zititest/tests/iperf_test.go @@ -0,0 +1,136 @@ +/* + (c) Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "fmt" + "github.com/openziti/fablab/kernel/lib" + "github.com/openziti/fablab/kernel/model" + "github.com/stretchr/testify/require" + "testing" + "time" +) + +func TestIPerf(t *testing.T) { + allZetHostedFailed := true + allZetClientsFailed := true + + t.Run("iperf-tests", func(t *testing.T) { + t.Run("ert-hosted", func(t *testing.T) { + t.Parallel() + + for _, clientType := range []string{"ert", "zet", "ziti-tunnel"} { + for _, encrypted := range []bool{true, false} { + for _, reversed := range []bool{true, false} { + success := testIPerf(t, clientType, "ert", encrypted, reversed) + if success && clientType == "zet" { + allZetClientsFailed = false + } + } + } + } + }) + + t.Run("zet-hosted", func(t *testing.T) { + t.Parallel() + + for _, clientType := range []string{"zet", "ziti-tunnel", "ert"} { + for _, encrypted := range []bool{true, false} { + for _, reversed := range []bool{true, false} { + success := testIPerf(t, clientType, "zet", encrypted, reversed) + if success { + allZetHostedFailed = false + if clientType == "zet" { + allZetClientsFailed = false + } + } + } + } + } + }) + + //t.Run("ziti-tunnel-hosted", func(t *testing.T) { + // t.Parallel() + // + // for _, clientType := range []string{"ziti-tunnel", "ert", "zet"} { + // for _, encrypted := range []bool{true, false} { + // for _, reversed := range []bool{true, false} { + // success := testIPerf(t, clientType, "ziti-tunnel", encrypted, reversed) + // if success && clientType == "zet" { + // allZetClientsFailed = false + // } + // } + // } + // } + //}) + }) + + req := require.New(t) + req.False(allZetHostedFailed, "all zet hosted iperf tests should not failed, indicates bigger issue") + req.False(allZetClientsFailed, "all zet client iperf tests should not failed, indicates bigger issue") +} + +func testIPerf(t *testing.T, hostSelector string, hostType string, encrypted bool, reversed bool) bool { + encDesk := "encrypted" + if !encrypted { + encDesk = "unencrypted" + } + + direction := "->" + if reversed { + direction = "<-" + } + + success := false + + t.Run(fmt.Sprintf("(%s%s%s)-%v", hostSelector, direction, hostType, encDesk), func(t *testing.T) { + host, err := model.GetModel().SelectHost("." + hostSelector + "-client") + req := require.New(t) + req.NoError(err) + + urlExtra := "" + if !encrypted { + urlExtra = "-unencrypted" + } + + addr := fmt.Sprintf("iperf-%s%s.ziti", hostType, urlExtra) + + extraOptions := "" + if reversed { + extraOptions += " -R" + } + + cmd := fmt.Sprintf(`set -o pipefail; iperf3 -c %s -t 10 %s`, addr, extraOptions) + + sshConfigFactory := lib.NewSshConfigFactory(host) + o, err := lib.RemoteExecAllWithTimeout(sshConfigFactory, 20*time.Second, cmd) + if hostType == "zet" && err != nil { + t.Skipf("zet hosted iperf test failed [%v]", err.Error()) + return + } + + if hostSelector == "zet" && err != nil { + t.Skipf("zet client iperf test failed [%v]", err.Error()) + return + } + + t.Log(o) + req.NoError(err) + success = true + }) + return success +} diff --git a/zititest/zitilab/component_controller.go b/zititest/zitilab/component_controller.go index fea22a1a3..d5af30204 100644 --- a/zititest/zitilab/component_controller.go +++ b/zititest/zitilab/component_controller.go @@ -47,7 +47,7 @@ type ControllerType struct { } func (self *ControllerType) GetActions() map[string]model.ComponentAction { - if len(self.actions.AsMap()) == 0 { + if len(self.actions.AsMap()) != 4 { self.actions.Put(model.ComponentActionStop, model.ComponentActionF(self.Stop)) self.actions.Put(model.ComponentActionStart, model.ComponentActionF(self.Start)) self.actions.Put(model.ComponentActionStageFiles, model.ComponentActionF(self.StageFiles)) diff --git a/zititest/zitilab/component_iperf.go b/zititest/zitilab/component_iperf.go new file mode 100644 index 000000000..af20df11d --- /dev/null +++ b/zititest/zitilab/component_iperf.go @@ -0,0 +1,83 @@ +/* + Copyright 2019 NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package zitilab + +import ( + "fmt" + "github.com/openziti/fablab/kernel/lib" + "github.com/openziti/fablab/kernel/model" + "github.com/sirupsen/logrus" + "strings" +) + +var _ model.ComponentType = (*IPerfServerType)(nil) + +type IPerfServerType struct { + Port uint16 +} + +func (self *IPerfServerType) Dump() any { + return map[string]string{ + "type_id": "iperf-server", + "port": fmt.Sprintf("%v", self.GetPort()), + } +} + +func (self *IPerfServerType) GetPort() uint16 { + if self.Port == 0 { + return 5201 + } + return self.Port +} + +func (self *IPerfServerType) getProcessFilter(*model.Component) func(string) bool { + return func(s string) bool { + return strings.Contains(s, fmt.Sprintf("iperf3 -s -p %v", self.GetPort())) + } +} + +func (self *IPerfServerType) IsRunning(_ model.Run, c *model.Component) (bool, error) { + factory := lib.NewSshConfigFactory(c.GetHost()) + pids, err := lib.FindProcesses(factory, self.getProcessFilter(c)) + if err != nil { + return false, err + } + return len(pids) > 0, nil +} + +func (self *IPerfServerType) Start(_ model.Run, c *model.Component) error { + factory := lib.NewSshConfigFactory(c.GetHost()) + + logsPath := fmt.Sprintf("/home/%s/logs/%s.log", factory.User(), c.Id) + serviceCmd := fmt.Sprintf("nohup iperf3 -s -p %v > %s 2>&1 &", self.GetPort(), logsPath) + + value, err := lib.RemoteExec(factory, serviceCmd) + if err != nil { + return err + } + + if len(value) > 0 { + logrus.Infof("output [%s]", strings.Trim(value, " \t\r\n")) + } + + return nil +} + +func (self *IPerfServerType) Stop(_ model.Run, c *model.Component) error { + factory := lib.NewSshConfigFactory(c.GetHost()) + return lib.RemoteKillFilterF(factory, self.getProcessFilter(c)) +}