From 51e111c0f58ad96ec304b76922dbfcfbacf8645f Mon Sep 17 00:00:00 2001 From: Richard Waller Date: Wed, 29 Apr 2020 18:32:39 +0100 Subject: [PATCH 1/2] test: correct test Signed-off-by: Richard Waller --- cmd/cli/main_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/cli/main_test.go b/cmd/cli/main_test.go index a470ef06..38bc7a74 100644 --- a/cmd/cli/main_test.go +++ b/cmd/cli/main_test.go @@ -152,9 +152,9 @@ func testCreateProjectFromTemplate(t *testing.T) { "--username="+test.GHEUsername, "--password=badpassword", ) - out, err := cmd.Output() + out, err := cmd.CombinedOutput() assert.NotNil(t, err) - assert.Equal(t, "", string(out)) + assert.Contains(t, string(out), "401 Unauthorized") }) } @@ -193,7 +193,7 @@ func testSuccessfulAddAndDeleteTemplateRepos(t *testing.T) { assert.Nil(t, removeErr) assert.NotContains(t, string(removeOut), test.PublicGHDevfileURL) }) - t.Run("cwctl templates repos add --url "+ + t.Run("cwctl templates repos add --url --username --password"+ "\n then cwctl templates repos remove --url", func(t *testing.T) { if !test.UsingOwnGHECredentials { t.Skip("skipping this test because you haven't set GitHub credentials needed for this test") From bda6918d03f9cf7dce64cc8bcc4202e33446b2df Mon Sep 17 00:00:00 2001 From: Richard Waller Date: Wed, 29 Apr 2020 18:36:27 +0100 Subject: [PATCH 2/2] feat: cwctl templates repos add can pass GH PAT to PFE Signed-off-by: Richard Waller --- README.md | 5 ++-- cmd/cli/main_test.go | 53 +++++++++++++++++++++++++++++++-- pkg/actions/commands.go | 5 ++++ pkg/actions/templates.go | 10 +++++-- pkg/apiroutes/templates.go | 10 +++---- pkg/apiroutes/templates_test.go | 20 ++++++++----- pkg/test/globals.go | 3 ++ pkg/utils/download.go | 5 ++-- pkg/utils/templates.go | 27 +++++++++++++++++ 9 files changed, 117 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 1f6a05d2..b8af3290 100644 --- a/README.md +++ b/README.md @@ -327,8 +327,9 @@ Subcommands:
> --url - URL to template repository index.json > --name - Custom name for template repository > --description - Custom description for template repository -> --username - GitHub username (required if accessing the provided URL requires GitHub authentication) -> --password - GitHub password (required if accessing the provided URL requires GitHub authentication) +> --username - GitHub username (required if accessing the provided URL requires GitHub authentication and you do not provide --personalAccessToken) +> --password - GitHub password (required if accessing the provided URL requires GitHub authentication and you do not provide --personalAccessToken) +> --personalAccessToken - GitHub personal access token (required if accessing the provided URL requires GitHub authentication and you do not provide --username and --password) ### version diff --git a/cmd/cli/main_test.go b/cmd/cli/main_test.go index 38bc7a74..c8ecd44e 100644 --- a/cmd/cli/main_test.go +++ b/cmd/cli/main_test.go @@ -208,6 +208,27 @@ func testSuccessfulAddAndDeleteTemplateRepos(t *testing.T) { assert.Nil(t, err) assert.Contains(t, string(out), test.GHEDevfileURL) + removeCmd := exec.Command(cwctl, "templates", "repos", "remove", + "--url="+test.GHEDevfileURL, + ) + removeOut, removeErr := removeCmd.Output() + assert.Nil(t, removeErr) + assert.NotContains(t, string(removeOut), test.GHEDevfileURL) + }) + t.Run("cwctl templates repos add --url --personalAccessToken"+ + "\n then cwctl templates repos remove --url", func(t *testing.T) { + if !test.UsingOwnGHECredentials { + t.Skip("skipping this test because you haven't set GitHub credentials needed for this test") + } + + cmd := exec.Command(cwctl, "templates", "repos", "add", + "--url="+test.GHEDevfileURL, + "--personalAccessToken="+test.GHEPersonalAccessToken, + ) + out, err := cmd.Output() + assert.Nil(t, err) + assert.Contains(t, string(out), test.GHEDevfileURL) + removeCmd := exec.Command(cwctl, "templates", "repos", "remove", "--url="+test.GHEDevfileURL, ) @@ -222,8 +243,36 @@ func testFailToAddTemplateRepo(t *testing.T) { cmd := exec.Command(cwctl, "templates", "repos", "add", "--url=https://raw.githubusercontent.com/kabanero-io/codewind-templates/aad4bafc14e1a295fb8e462c20fe8627248609a3/devfiles/NOT_INDEX_JSON", ) - out, err := cmd.Output() + out, err := cmd.CombinedOutput() + assert.Nil(t, err) + assert.Contains(t, string(out), "does not point to a JSON file of the correct form") + }) + t.Run("cwctl templates repos add --url --personalAccessToken --username", func(t *testing.T) { + cmd := exec.Command(cwctl, "templates", "repos", "add", + "--url="+test.GHEDevfileURL, + "--personalAccessToken=validPersonalAccessToken", + "--username=validUsername", + ) + out, err := cmd.CombinedOutput() + assert.Nil(t, err) + assert.Contains(t, string(out), "received credentials for multiple authentication methods") + }) + t.Run("cwctl templates repos add --url --username", func(t *testing.T) { + cmd := exec.Command(cwctl, "templates", "repos", "add", + "--url="+test.GHEDevfileURL, + "--username=validUsername", + ) + out, err := cmd.CombinedOutput() + assert.Nil(t, err) + assert.Contains(t, string(out), "received username but no password") + }) + t.Run("cwctl templates repos add --url --password", func(t *testing.T) { + cmd := exec.Command(cwctl, "templates", "repos", "add", + "--url="+test.GHEDevfileURL, + "--password=validPassword", + ) + out, err := cmd.CombinedOutput() assert.Nil(t, err) - assert.Equal(t, string(out), "") + assert.Contains(t, string(out), "received password but no username") }) } diff --git a/pkg/actions/commands.go b/pkg/actions/commands.go index 51352098..54df8eb4 100755 --- a/pkg/actions/commands.go +++ b/pkg/actions/commands.go @@ -526,6 +526,11 @@ func Commands() { Usage: "GitHub password", Required: false, }, + cli.StringFlag{ + Name: "personalAccessToken", + Usage: "GitHub personal access token", + Required: false, + }, }, Action: func(c *cli.Context) error { AddTemplateRepo(c) diff --git a/pkg/actions/templates.go b/pkg/actions/templates.go index 916f1c65..2ad64ea6 100644 --- a/pkg/actions/templates.go +++ b/pkg/actions/templates.go @@ -70,11 +70,15 @@ func AddTemplateRepo(c *cli.Context) { name := c.String("name") username := c.String("username") password := c.String("password") + personalAccessToken := c.String("personalAccessToken") - gitCredentials := utils.GitCredentials{ - Username: username, - Password: password, + gitCredentials, err := utils.ExtractGitCredentials(username, password, personalAccessToken) + if err != nil { + templateErr := &TemplateError{errOpAddRepo, err, err.Error()} + HandleTemplateError(templateErr) + return } + conID := strings.TrimSpace(strings.ToLower(c.String("conid"))) repos, err := apiroutes.AddTemplateRepo(conID, url, desc, name, gitCredentials) if err != nil { diff --git a/pkg/apiroutes/templates.go b/pkg/apiroutes/templates.go index b8e86465..498a1cc2 100644 --- a/pkg/apiroutes/templates.go +++ b/pkg/apiroutes/templates.go @@ -41,10 +41,10 @@ type ( // RepoCreationOptions is the request body for PFE's POST /templates/repositories API RepoCreationOptions struct { - URL string `json:"url"` - Description string `json:"description"` - Name string `json:"name"` - GitCredentials utils.GitCredentials `json:"gitCredentials"` + URL string `json:"url,omitempty"` + Description string `json:"description,omitempty"` + Name string `json:"name,omitempty"` + GitCredentials *utils.GitCredentials `json:"gitCredentials,omitempty"` } // RepoOperation represents a requested operation on a template repository. @@ -187,7 +187,7 @@ func GetTemplateRepos(conID string) ([]utils.TemplateRepo, error) { // AddTemplateRepo adds a template repo to PFE and // returns the new list of existing repos -func AddTemplateRepo(conID, URL, description, name string, gitCredentials utils.GitCredentials) ([]utils.TemplateRepo, error) { +func AddTemplateRepo(conID, URL, description, name string, gitCredentials *utils.GitCredentials) ([]utils.TemplateRepo, error) { if _, err := url.ParseRequestURI(URL); err != nil { return nil, fmt.Errorf("Error: '%s' is not a valid URL", URL) } diff --git a/pkg/apiroutes/templates_test.go b/pkg/apiroutes/templates_test.go index 1a6a8757..5adcfec5 100644 --- a/pkg/apiroutes/templates_test.go +++ b/pkg/apiroutes/templates_test.go @@ -168,7 +168,7 @@ func TestFailuresAddTemplateRepo(t *testing.T) { } for name, test := range tests { t.Run(name, func(t *testing.T) { - got, err := AddTemplateRepo("local", test.inURL, test.inDescription, "template-name", utils.GitCredentials{}) + got, err := AddTemplateRepo("local", test.inURL, test.inDescription, "template-name", nil) assert.IsType(t, test.wantedType, got, "got: %v", got) assert.Equal(t, test.wantedErr, err) }) @@ -206,20 +206,26 @@ func TestSuccessfulAddAndDeleteTemplateRepos(t *testing.T) { tests := map[string]struct { skip bool inURL string - inGitCredentials utils.GitCredentials + inGitCredentials *utils.GitCredentials }{ - "PublicGHDevFileURL": { - inURL: cwTest.PublicGHDevfileURL, - inGitCredentials: utils.GitCredentials{}, + "public GH devfile URL": { + inURL: cwTest.PublicGHDevfileURL, }, - "GHEDevfileURL": { + "GHE devfile URL with GHE basic credentials": { skip: !cwTest.UsingOwnGHECredentials, inURL: cwTest.GHEDevfileURL, - inGitCredentials: utils.GitCredentials{ + inGitCredentials: &utils.GitCredentials{ Username: test.GHEUsername, Password: test.GHEPassword, }, }, + "GHE devfile URL with GHE personal access token": { + skip: !cwTest.UsingOwnGHECredentials, + inURL: cwTest.GHEDevfileURL, + inGitCredentials: &utils.GitCredentials{ + PersonalAccessToken: test.GHEPersonalAccessToken, + }, + }, } for name, test := range tests { if test.skip { diff --git a/pkg/test/globals.go b/pkg/test/globals.go index 5f6e6a12..37b1bfe2 100644 --- a/pkg/test/globals.go +++ b/pkg/test/globals.go @@ -29,5 +29,8 @@ const GHEUsername = "INSERT YOUR OWN: e.g. foo.bar@foobar.com" // GHEPassword is a password that passes the auth required to access a GHERepoURL const GHEPassword = "INSERT YOUR OWN: e.g. 1234kljfdsjfaleru29348spodkfj445" +// GHEPersonalAccessToken is a personal access token that passes the auth required to access a GHERepoURL +const GHEPersonalAccessToken = "INSERT YOUR OWN" + // UsingOwnGHECredentials should be set to true if you want to run tests using the credentials above const UsingOwnGHECredentials = false diff --git a/pkg/utils/download.go b/pkg/utils/download.go index 67d1454d..39448e70 100644 --- a/pkg/utils/download.go +++ b/pkg/utils/download.go @@ -28,8 +28,9 @@ import ( type ( // GitCredentials : credentials to access GitHub or GitHubEnterprise GitCredentials struct { - Username string `json:"username"` - Password string `json:"password"` + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` + PersonalAccessToken string `json:"personalAccessToken,omitempty"` } ) diff --git a/pkg/utils/templates.go b/pkg/utils/templates.go index 617b6fe7..dc02cb4c 100644 --- a/pkg/utils/templates.go +++ b/pkg/utils/templates.go @@ -11,6 +11,8 @@ package utils +import "fmt" + type ( // TemplateRepo represents a template repository. TemplateRepo struct { @@ -102,3 +104,28 @@ func OnDeleteTemplateRepo(extensions []Extension, url string, repos []TemplateRe } } } + +// ExtractGitCredentials extracts and formats git credentials from the provided arguments +func ExtractGitCredentials(username, password, personalAccessToken string) (*GitCredentials, error) { + if personalAccessToken != "" && (username != "" || password != "") { + return nil, fmt.Errorf("received credentials for multiple authentication methods") + } + if username != "" && password == "" { + return nil, fmt.Errorf("received username but no password") + } + if password != "" && username == "" { + return nil, fmt.Errorf("received password but no username") + } + if username != "" && password != "" { + return &GitCredentials{ + Username: username, + Password: password, + }, nil + } + if personalAccessToken != "" { + return &GitCredentials{ + PersonalAccessToken: personalAccessToken, + }, nil + } + return nil, nil +}