From fafc0c00b291c1b1bb9761e0fc9379cf9a29d984 Mon Sep 17 00:00:00 2001 From: andrew freitag <49578051+friday963@users.noreply.github.com> Date: Fri, 29 Sep 2023 11:17:32 -0400 Subject: [PATCH 1/6] init --- .srl01.clab.yml.bak | 11 ++++++++ cmd/root.go | 20 ++++++++++++++- utils/http.go | 62 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 .srl01.clab.yml.bak create mode 100644 utils/http.go diff --git a/.srl01.clab.yml.bak b/.srl01.clab.yml.bak new file mode 100644 index 000000000..0af99249d --- /dev/null +++ b/.srl01.clab.yml.bak @@ -0,0 +1,11 @@ +# topology documentation: http://containerlab.dev/lab-examples/single-srl/ +name: srl01 + +topology: + kinds: + srl: + type: ixrd3 + image: ghcr.io/nokia/srlinux + nodes: + srl: + kind: srl diff --git a/cmd/root.go b/cmd/root.go index 57ad93f44..00a8d46fc 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -6,12 +6,14 @@ package cmd import ( "errors" + "fmt" "os" "path/filepath" "time" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" + "github.com/srl-labs/containerlab/utils" ) var ( @@ -106,12 +108,28 @@ func getTopoFilePath(cmd *cobra.Command) error { return nil } + var err error + if utils.IsHttpUri(topo){ + switch { + case utils.IsGitHubURL(topo): + err := utils.CheckSuffix(topo) + if err != nil { + return err + } + rawUrl := utils.GetRawURL(topo) + topo = utils.GetFileName(topo) + utils.DownloadFile(rawUrl, topo) + // utils.DownloadFile(tempTopo, topo) + default: + return fmt.Errorf("unsupported URL: %s", topo) + } + } + // if topo or name flags have been provided, don't try to derive the topo file if topo != "" || name != "" { return nil } - var err error log.Debugf("trying to find topology files automatically") diff --git a/utils/http.go b/utils/http.go new file mode 100644 index 000000000..ab50d2605 --- /dev/null +++ b/utils/http.go @@ -0,0 +1,62 @@ +package utils + +import ( + "strings" + "errors" + "net/http" + "log" + "io" + "os" +) + +// write a methd to replace "/blob" with "" + + +func GetRawURL(url string) string { + + raw:=strings.Replace(url, "github.com", "raw.githubusercontent.com", 1) + return strings.Replace(raw, "/blob", "", 1) +} + +func IsGitHubURL(url string) bool { + return strings.Contains(url, "github") + +} + +func GetFileName(url string) string { + split := strings.Split(url, "/") + return split[len(split)-1] +} + +func CheckSuffix(url string) error { + // check if topo ends with either .yml or .yaml + if !strings.HasSuffix(url, ".yml") && !strings.HasSuffix(url, ".yaml") { + return errors.New("valid URL passed in as topology file, but does not end with .yml or .yaml, endpoint must be an actual topology file") + } + return nil +} + +func DownloadFile(url string, ouputFileName string) { + // Get the data + resp, err := http.Get(url) + if err != nil { + log.Fatal(err) + } + if resp.StatusCode != 200 { + log.Fatal("URL does not exist") + } + defer resp.Body.Close() + + // Create the file + out, err := os.Create(ouputFileName) + if err != nil { + log.Fatal(err) + } + defer out.Close() + + // Write the body to file + _, err = io.Copy(out, resp.Body) + if err != nil { + log.Fatal(err) + } +} From 857ecd4d187a57e0ee66120e48bc2266264bb70a Mon Sep 17 00:00:00 2001 From: andrew freitag <49578051+friday963@users.noreply.github.com> Date: Sat, 7 Oct 2023 17:10:21 -0400 Subject: [PATCH 2/6] update tests --- cmd/root.go | 3 +- utils/http.go | 26 ++++---- utils/http_test.go | 130 +++++++++++++++++++++++++++++++++++++++ utils/temp_test_file.txt | 1 + 4 files changed, 146 insertions(+), 14 deletions(-) create mode 100644 utils/http_test.go create mode 100644 utils/temp_test_file.txt diff --git a/cmd/root.go b/cmd/root.go index 00a8d46fc..e691a7a89 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -119,9 +119,8 @@ func getTopoFilePath(cmd *cobra.Command) error { rawUrl := utils.GetRawURL(topo) topo = utils.GetFileName(topo) utils.DownloadFile(rawUrl, topo) - // utils.DownloadFile(tempTopo, topo) default: - return fmt.Errorf("unsupported URL: %s", topo) + return fmt.Errorf("unsupported git repositoy: %s", topo) } } diff --git a/utils/http.go b/utils/http.go index ab50d2605..7596739d5 100644 --- a/utils/http.go +++ b/utils/http.go @@ -4,18 +4,17 @@ import ( "strings" "errors" "net/http" - "log" "io" "os" ) -// write a methd to replace "/blob" with "" - func GetRawURL(url string) string { - - raw:=strings.Replace(url, "github.com", "raw.githubusercontent.com", 1) - return strings.Replace(raw, "/blob", "", 1) + if strings.Contains(url, "github.com") { + raw:=strings.Replace(url, "github.com", "raw.githubusercontent.com", 1) + return strings.Replace(raw, "/blob", "", 1) + } + return url } func IsGitHubURL(url string) bool { @@ -28,35 +27,38 @@ func GetFileName(url string) string { return split[len(split)-1] } +// required global variable for tests, otherwise comparison operator fails as error instances were not equal +var ErrInvalidSuffix = errors.New("valid URL passed in as topology file, but does not end with .yml or .yaml, endpoint must be an actual topology file") func CheckSuffix(url string) error { // check if topo ends with either .yml or .yaml if !strings.HasSuffix(url, ".yml") && !strings.HasSuffix(url, ".yaml") { - return errors.New("valid URL passed in as topology file, but does not end with .yml or .yaml, endpoint must be an actual topology file") + return ErrInvalidSuffix } return nil } -func DownloadFile(url string, ouputFileName string) { +func DownloadFile(url string, ouputFileName string) error { // Get the data resp, err := http.Get(url) if err != nil { - log.Fatal(err) + return err } if resp.StatusCode != 200 { - log.Fatal("URL does not exist") + return errors.New("URL does not exist") } defer resp.Body.Close() // Create the file out, err := os.Create(ouputFileName) if err != nil { - log.Fatal(err) + return err } defer out.Close() // Write the body to file _, err = io.Copy(out, resp.Body) if err != nil { - log.Fatal(err) + return err } + return nil } diff --git a/utils/http_test.go b/utils/http_test.go new file mode 100644 index 000000000..495ef7f33 --- /dev/null +++ b/utils/http_test.go @@ -0,0 +1,130 @@ +package utils + +import ( + "net/http" + "net/http/httptest" + "os" + "testing" +) + +func TestIsGithubURL(t *testing.T) { + // tests that github urls are detected + var tests = []struct { + input string + expected bool + }{ + {"github.com", true}, + {"github.com/containers/containerlab/blob/master/README.md", true}, + {"google.com/containers", false}, + {"google.com/containers/containerlab/blob/master/README.md", false}, + {"gitlab.com/containers", false}, + {"raw.githubusercontent.com/containers", true}, + } + for _, test := range tests { + if output := IsGitHubURL(test.input); output != test.expected { + t.Error("Test Failed: {} inputted, {} expected, recieved: {}", test.input, test.expected, output) + } + } +} + +func TestGetRawURL(t *testing.T) { + // tests that github urls are converted to raw urls evething else is left as is + var tests = []struct { + input string + expected string + }{ + {"github.com", "raw.githubusercontent.com"}, + {"github.com/containers/containerlab/blob/master/README.md", "raw.githubusercontent.com/containers/containerlab/master/README.md"}, + {"google.com/containers", "google.com/containers"}, + {"google.com/containers/containerlab/blob/master/README.md", "google.com/containers/containerlab/blob/master/README.md"}, + {"gitlab.com/containers", "gitlab.com/containers"}, + {"raw.githubusercontent.com/containers", "raw.githubusercontent.com/containers"}, + } + for _, test := range tests { + if output := GetRawURL(test.input); output != test.expected { + t.Error("Test Failed: {} inputted, {} expected, recieved: {}", test.input, test.expected, output) + } + } +} + +func TestGetFileName(t *testing.T) { + // tests for valid file name + var tests = []struct { + input string + expected string + }{ + {"github.com", "github.com"}, + {"github.com/containers/containerlab/blob/master/README.md", "README.md"}, + {"google.com/containers", "containers"}, + {"google.com/containers/containerlab/blob/master/README.md", "README.md"}, + {"gitlab.com/containers", "containers"}, + {"raw.githubusercontent.com/containers", "containers"}, + } + for _, test := range tests { + if output := GetFileName(test.input); output != test.expected { + t.Error("Test Failed: {} inputted, {} expected, recieved: {}", test.input, test.expected, output) + } + } +} + +func TestCheckSuffix(t *testing.T) { + // tests for valid suffix + var tests = []struct { + input string + expected error + }{ + {"github.com", ErrInvalidSuffix}, + {"github.com/containers/containerlab/blob/master/README.md", ErrInvalidSuffix}, + {"google.com/containers", ErrInvalidSuffix}, + {"google.com/containers/containerlab/blob/master/README.md", ErrInvalidSuffix}, + {"gitlab.com/containers", ErrInvalidSuffix}, + {"raw.githubusercontent.com/containers", ErrInvalidSuffix}, + {"github.com/containers/containerlab/blob/master/README.yml", nil}, + {"github.com/containers/containerlab/blob/master/README.yaml", nil}, + } + for _, test := range tests { + if output := CheckSuffix(test.input); output != test.expected { + t.Error("Test Failed: {} inputted, {} expected, recieved: {}", test.input, test.expected, output) + } + } +} + +func TestDownloadFile(t *testing.T) { + tempDir := os.TempDir() + // Create a mock HTTP server for testing + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Respond with a sample file content for testing + if r.URL.Path == "/valid" { + w.WriteHeader(http.StatusOK) + w.Write([]byte("This is a sample file content")) + } else { + w.WriteHeader(http.StatusNotFound) + } + })) + defer ts.Close() + + // Test case 1: Download a file that exists + outputFileName := tempDir + "/downloaded_file.txt" + err := DownloadFile(ts.URL+"/valid", outputFileName) + if err != nil { + t.Fatalf("Expected no error, but got: %v", err) + } + + // Check the content of the downloaded file + content, err := os.ReadFile(outputFileName) + if err != nil { + t.Fatalf("Failed to read the downloaded file: %v", err) + } + expectedContent := "This is a sample file content" + if string(content) != expectedContent { + t.Errorf("Expected content: %s, but got: %s", expectedContent, string(content)) + } + os.Remove(outputFileName) + // Test case 2: Download a file that does not exist (simulate a non-200 response) + outputFileName = tempDir + "/nonexistent_file.txt" + err = DownloadFile(ts.URL+"/nonexistent", outputFileName) + expectedErrorMsg := "URL does not exist" + if err == nil || err.Error() != expectedErrorMsg { + t.Errorf("Expected error message '%s', but got: %v", expectedErrorMsg, err) + } +} \ No newline at end of file diff --git a/utils/temp_test_file.txt b/utils/temp_test_file.txt new file mode 100644 index 000000000..51567bd59 --- /dev/null +++ b/utils/temp_test_file.txt @@ -0,0 +1 @@ +This is a sample file content \ No newline at end of file From 916f167585ab1c51a02f6b14568d98a4b9bf9c34 Mon Sep 17 00:00:00 2001 From: andrew freitag <49578051+friday963@users.noreply.github.com> Date: Sat, 7 Oct 2023 17:38:56 -0400 Subject: [PATCH 3/6] remove unecessary files --- .srl01.clab.yml.bak | 11 ----------- utils/temp_test_file.txt | 1 - 2 files changed, 12 deletions(-) delete mode 100644 .srl01.clab.yml.bak delete mode 100644 utils/temp_test_file.txt diff --git a/.srl01.clab.yml.bak b/.srl01.clab.yml.bak deleted file mode 100644 index 0af99249d..000000000 --- a/.srl01.clab.yml.bak +++ /dev/null @@ -1,11 +0,0 @@ -# topology documentation: http://containerlab.dev/lab-examples/single-srl/ -name: srl01 - -topology: - kinds: - srl: - type: ixrd3 - image: ghcr.io/nokia/srlinux - nodes: - srl: - kind: srl diff --git a/utils/temp_test_file.txt b/utils/temp_test_file.txt deleted file mode 100644 index 51567bd59..000000000 --- a/utils/temp_test_file.txt +++ /dev/null @@ -1 +0,0 @@ -This is a sample file content \ No newline at end of file From bc2a50a2ddcd8c2cfd889fdfbbeb499a4c34f324 Mon Sep 17 00:00:00 2001 From: andrew freitag <49578051+friday963@users.noreply.github.com> Date: Mon, 9 Oct 2023 20:09:01 -0400 Subject: [PATCH 4/6] remove dup code --- cmd/root.go | 6 +++-- utils/http.go | 31 ------------------------ utils/http_test.go | 60 ---------------------------------------------- 3 files changed, 4 insertions(+), 93 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index e691a7a89..674095ac4 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -117,8 +117,10 @@ func getTopoFilePath(cmd *cobra.Command) error { return err } rawUrl := utils.GetRawURL(topo) - topo = utils.GetFileName(topo) - utils.DownloadFile(rawUrl, topo) + // topo = utils.GetFileName(topo) + topo = utils.FilenameForURL(topo) + // utils.DownloadFile(rawUrl, topo) + utils.CopyFileContents(rawUrl, topo, 0754) default: return fmt.Errorf("unsupported git repositoy: %s", topo) } diff --git a/utils/http.go b/utils/http.go index 7596739d5..c5d446c1f 100644 --- a/utils/http.go +++ b/utils/http.go @@ -22,11 +22,6 @@ func IsGitHubURL(url string) bool { } -func GetFileName(url string) string { - split := strings.Split(url, "/") - return split[len(split)-1] -} - // required global variable for tests, otherwise comparison operator fails as error instances were not equal var ErrInvalidSuffix = errors.New("valid URL passed in as topology file, but does not end with .yml or .yaml, endpoint must be an actual topology file") func CheckSuffix(url string) error { @@ -36,29 +31,3 @@ func CheckSuffix(url string) error { } return nil } - -func DownloadFile(url string, ouputFileName string) error { - // Get the data - resp, err := http.Get(url) - if err != nil { - return err - } - if resp.StatusCode != 200 { - return errors.New("URL does not exist") - } - defer resp.Body.Close() - - // Create the file - out, err := os.Create(ouputFileName) - if err != nil { - return err - } - defer out.Close() - - // Write the body to file - _, err = io.Copy(out, resp.Body) - if err != nil { - return err - } - return nil -} diff --git a/utils/http_test.go b/utils/http_test.go index 495ef7f33..53b4ef39a 100644 --- a/utils/http_test.go +++ b/utils/http_test.go @@ -47,26 +47,6 @@ func TestGetRawURL(t *testing.T) { } } -func TestGetFileName(t *testing.T) { - // tests for valid file name - var tests = []struct { - input string - expected string - }{ - {"github.com", "github.com"}, - {"github.com/containers/containerlab/blob/master/README.md", "README.md"}, - {"google.com/containers", "containers"}, - {"google.com/containers/containerlab/blob/master/README.md", "README.md"}, - {"gitlab.com/containers", "containers"}, - {"raw.githubusercontent.com/containers", "containers"}, - } - for _, test := range tests { - if output := GetFileName(test.input); output != test.expected { - t.Error("Test Failed: {} inputted, {} expected, recieved: {}", test.input, test.expected, output) - } - } -} - func TestCheckSuffix(t *testing.T) { // tests for valid suffix var tests = []struct { @@ -88,43 +68,3 @@ func TestCheckSuffix(t *testing.T) { } } } - -func TestDownloadFile(t *testing.T) { - tempDir := os.TempDir() - // Create a mock HTTP server for testing - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // Respond with a sample file content for testing - if r.URL.Path == "/valid" { - w.WriteHeader(http.StatusOK) - w.Write([]byte("This is a sample file content")) - } else { - w.WriteHeader(http.StatusNotFound) - } - })) - defer ts.Close() - - // Test case 1: Download a file that exists - outputFileName := tempDir + "/downloaded_file.txt" - err := DownloadFile(ts.URL+"/valid", outputFileName) - if err != nil { - t.Fatalf("Expected no error, but got: %v", err) - } - - // Check the content of the downloaded file - content, err := os.ReadFile(outputFileName) - if err != nil { - t.Fatalf("Failed to read the downloaded file: %v", err) - } - expectedContent := "This is a sample file content" - if string(content) != expectedContent { - t.Errorf("Expected content: %s, but got: %s", expectedContent, string(content)) - } - os.Remove(outputFileName) - // Test case 2: Download a file that does not exist (simulate a non-200 response) - outputFileName = tempDir + "/nonexistent_file.txt" - err = DownloadFile(ts.URL+"/nonexistent", outputFileName) - expectedErrorMsg := "URL does not exist" - if err == nil || err.Error() != expectedErrorMsg { - t.Errorf("Expected error message '%s', but got: %v", expectedErrorMsg, err) - } -} \ No newline at end of file From 59802fe0939c09b46f60de6828a8299c73af40d3 Mon Sep 17 00:00:00 2001 From: andrew freitag <49578051+friday963@users.noreply.github.com> Date: Mon, 9 Oct 2023 20:09:45 -0400 Subject: [PATCH 5/6] update imports --- utils/http.go | 3 --- utils/http_test.go | 3 --- 2 files changed, 6 deletions(-) diff --git a/utils/http.go b/utils/http.go index c5d446c1f..64249d764 100644 --- a/utils/http.go +++ b/utils/http.go @@ -3,9 +3,6 @@ package utils import ( "strings" "errors" - "net/http" - "io" - "os" ) diff --git a/utils/http_test.go b/utils/http_test.go index 53b4ef39a..46d3c4602 100644 --- a/utils/http_test.go +++ b/utils/http_test.go @@ -1,9 +1,6 @@ package utils import ( - "net/http" - "net/http/httptest" - "os" "testing" ) From 5411f339e1c5a6dd3bd8bee85f4d477f978df963 Mon Sep 17 00:00:00 2001 From: andrew freitag <49578051+friday963@users.noreply.github.com> Date: Mon, 9 Oct 2023 20:11:44 -0400 Subject: [PATCH 6/6] remove unused from root --- cmd/root.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index 674095ac4..d39704bbe 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -117,9 +117,7 @@ func getTopoFilePath(cmd *cobra.Command) error { return err } rawUrl := utils.GetRawURL(topo) - // topo = utils.GetFileName(topo) topo = utils.FilenameForURL(topo) - // utils.DownloadFile(rawUrl, topo) utils.CopyFileContents(rawUrl, topo, 0754) default: return fmt.Errorf("unsupported git repositoy: %s", topo)