Skip to content

Commit

Permalink
Cherry-pick #4571 #5171 #5190 (#5191)
Browse files Browse the repository at this point in the history
* Modified to use Git with PAT (#4571)

* fix to read PAT settings from file

Signed-off-by: sZma5a <sZma5a@geekers.io>
Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: Your Name <you@example.com>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>
Signed-off-by: swallow <masaaki@haribote-lab.net>
Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* piped

Signed-off-by: sZma5a <sZma5a@geekers.io>
Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: Your Name <you@example.com>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>
Signed-off-by: swallow <masaaki@haribote-lab.net>
Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* include  PAT information in URL

Signed-off-by: sZma5a <sZma5a@geekers.io>
Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: Your Name <you@example.com>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>
Signed-off-by: swallow <masaaki@haribote-lab.net>
Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* fix: modification of conditional branching

Co-authored-by: sivchari <shibuuuu5@gmail.com>
Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: Your Name <you@example.com>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>
Signed-off-by: swallow <masaaki@haribote-lab.net>
Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* fix: corrected error in error message

Co-authored-by: sivchari <shibuuuu5@gmail.com>
Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: Your Name <you@example.com>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>
Signed-off-by: swallow <masaaki@haribote-lab.net>
Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* fix: integration of mask function

Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: Your Name <you@example.com>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>
Signed-off-by: swallow <masaaki@haribote-lab.net>
Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* fix: make validation test

Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: Your Name <you@example.com>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>
Signed-off-by: swallow <masaaki@haribote-lab.net>
Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* fix: function name

Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: Your Name <you@example.com>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>
Signed-off-by: swallow <masaaki@haribote-lab.net>
Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* fix: rename function for validation PAT

Signed-off-by: Your Name <you@example.com>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>
Signed-off-by: swallow <masaaki@haribote-lab.net>
Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* fix: fix test code as pointed out in the review

Signed-off-by: Your Name <you@example.com>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>
Signed-off-by: swallow <masaaki@haribote-lab.net>
Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* feat: add explan
for git personal access token in document

Signed-off-by: Your Name <you@example.com>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>
Signed-off-by: swallow <masaaki@haribote-lab.net>
Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* fix: change required in documentation

Signed-off-by: sZma5a <masaaki.haribote@gmail.com>
Signed-off-by: swallow <masaaki@haribote-lab.net>
Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* fix: change return value

Signed-off-by: sZma5a <masaaki.haribote@gmail.com>
Signed-off-by: swallow <masaaki@haribote-lab.net>
Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* fix: add test case

Signed-off-by: sZma5a <masaaki.haribote@gmail.com>
Signed-off-by: swallow <masaaki@haribote-lab.net>
Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* fix: fix test

Signed-off-by: swallow <masaaki@haribote-lab.net>
Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* fix: PipedGit struct to use password
authentication instead of personal access token

Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* fix to read PAT settings from file

Signed-off-by: sZma5a <sZma5a@geekers.io>
Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: Your Name <you@example.com>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>
Signed-off-by: swallow <masaaki@haribote-lab.net>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>
Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* piped

Signed-off-by: sZma5a <sZma5a@geekers.io>
Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: Your Name <you@example.com>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>
Signed-off-by: swallow <masaaki@haribote-lab.net>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>
Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* fix: integration of mask function

Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: Your Name <you@example.com>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>
Signed-off-by: swallow <masaaki@haribote-lab.net>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>
Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* fix: make validation test

Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: Your Name <you@example.com>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>
Signed-off-by: swallow <masaaki@haribote-lab.net>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>
Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* fix: function name

Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: Your Name <you@example.com>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>
Signed-off-by: swallow <masaaki@haribote-lab.net>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>
Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* fix: rename function for validation PAT

Signed-off-by: Your Name <you@example.com>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>
Signed-off-by: swallow <masaaki@haribote-lab.net>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>
Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* fix: fix test

Signed-off-by: swallow <masaaki@haribote-lab.net>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>
Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* fix: PipedGit struct to use password
authentication instead of personal access token

Signed-off-by: sZma5a <masaaki.haribote@gmail.com>
Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* Fix Git authentication configuration

Signed-off-by: sZma5a <masaaki.haribote@gmail.com>
Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* Update password authentication configuration

Signed-off-by: sZma5a <masaaki.haribote@gmail.com>
Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* Fix error variable name

Signed-off-by: sZma5a <masaaki.haribote@gmail.com>
Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* Fix rename password

Signed-off-by: sZma5a <masaaki.haribote@gmail.com>
Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* Refactor includePasswordAuthRemote function

Signed-off-by: sZma5a <masaaki.haribote@gmail.com>
Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* Update password authentication in clone test

Signed-off-by: sZma5a <masaaki.haribote@gmail.com>
Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* fix: delete PasswordAuth

Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* fix: remove unused PasswordAuth field and refactor password authentication in git client

Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* Remove unnecessary print statement in Validate function

Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* fix: fix code for rebase

Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* fix: remove unused GitPasswordAuth configuration

Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* feat: add password decoding for password in includePasswordRemote function

Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* fix: refactor Git password authentication method

Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* fix: update password encoding in TestCloneUsingPassword

Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* Update docs/content/en/docs-dev/user-guide/managing-piped/configuration-reference.md

Co-authored-by: Yoshiki Fujikane <40124947+ffjlabo@users.noreply.github.com>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* Update pkg/config/piped.go

Co-authored-by: Yoshiki Fujikane <40124947+ffjlabo@users.noreply.github.com>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* [wip] delete password

Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* [wip] not tested - change token to args from url

Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* Fix commented out test case

Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* Refactor authentication in git client

Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

* feat: add password decoding function and replace Password string

Signed-off-by: sZma5a <masaaki.haribote@gmail.com>

---------

Signed-off-by: sZma5a <sZma5a@geekers.io>
Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: Your Name <you@example.com>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>
Signed-off-by: swallow <masaaki@haribote-lab.net>
Co-authored-by: sZma5a <sZma5a@geekers.io>
Co-authored-by: sivchari <shibuuuu5@gmail.com>
Co-authored-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Co-authored-by: Your Name <you@example.com>
Co-authored-by: Yoshiki Fujikane <40124947+ffjlabo@users.noreply.github.com>
Signed-off-by: pipecd-bot <pipecd.dev@gmail.com>

* Use singleflight to clone/update repository cache (#5171)

Signed-off-by: Shinnosuke Sawada-Dazai <shin@warashi.dev>
Signed-off-by: pipecd-bot <pipecd.dev@gmail.com>

* Refactor the git clone (#5190)

Move the authArgs into the singleflight closure, as they are only used within it.

Signed-off-by: Shinnosuke Sawada-Dazai <shin@warashi.dev>
Signed-off-by: pipecd-bot <pipecd.dev@gmail.com>

---------

Signed-off-by: sZma5a <sZma5a@geekers.io>
Signed-off-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Signed-off-by: Your Name <you@example.com>
Signed-off-by: sZma5a <masaaki.haribote@gmail.com>
Signed-off-by: swallow <masaaki@haribote-lab.net>
Signed-off-by: pipecd-bot <pipecd.dev@gmail.com>
Signed-off-by: Shinnosuke Sawada-Dazai <shin@warashi.dev>
Co-authored-by: sZma5a <35451404+sZma5a@users.noreply.github.com>
Co-authored-by: sZma5a <sZma5a@geekers.io>
Co-authored-by: sivchari <shibuuuu5@gmail.com>
Co-authored-by: 鈴木 優耀 <suzuki_masaaki@cyberagent.co.jp>
Co-authored-by: Your Name <you@example.com>
Co-authored-by: Yoshiki Fujikane <40124947+ffjlabo@users.noreply.github.com>
Co-authored-by: Shinnosuke Sawada-Dazai <shin@warashi.dev>
  • Loading branch information
8 people authored Sep 5, 2024
1 parent 8c47ec6 commit 2f57e0a
Show file tree
Hide file tree
Showing 5 changed files with 212 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ spec:
| hostName | string | The hostname or IP address of the remote git server. Default is the same value with Host. | No |
| sshKeyFile | string | The path to the private ssh key file. This will be used to clone the source code of the specified git repositories. | No |
| sshKeyData | string | Base64 encoded string of SSH key. | No |
| password | string | The base64 encoded password for git used while cloning above Git repository. | No |

## GitRepository

Expand Down
14 changes: 14 additions & 0 deletions pkg/app/piped/cmd/piped/piped.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,11 +278,18 @@ func (p *piped) run(ctx context.Context, input cli.Input) (runErr error) {
})
}

password, err := cfg.Git.DecodedPassword()
if err != nil {
input.Logger.Error("failed to decode password", zap.Error(err))
return err
}

// Initialize git client.
gitOptions := []git.Option{
git.WithUserName(cfg.Git.Username),
git.WithEmail(cfg.Git.Email),
git.WithLogger(input.Logger),
git.WithPassword(password),
}
for _, repo := range cfg.GitHelmChartRepositories() {
if f := repo.SSHKeyFile; f != "" {
Expand Down Expand Up @@ -462,12 +469,19 @@ func (p *piped) run(ctx context.Context, input cli.Input) (runErr error) {

// Start running planpreview handler.
{
// Decode password for plan-preview feature.
password, err := cfg.Git.DecodedPassword()
if err != nil {
input.Logger.Error("failed to decode password", zap.Error(err))
return err
}
// Initialize a dedicated git client for plan-preview feature.
// Basically, this feature is an utility so it should not share any resource with the main components of piped.
gc, err := git.NewClient(
git.WithUserName(cfg.Git.Username),
git.WithEmail(cfg.Git.Email),
git.WithLogger(input.Logger),
git.WithPassword(password),
)
if err != nil {
input.Logger.Error("failed to initialize git client for plan-preview", zap.Error(err))
Expand Down
35 changes: 35 additions & 0 deletions pkg/config/piped.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@ func (s *PipedSpec) Validate() error {
if s.SyncInterval < 0 {
return errors.New("syncInterval must be greater than or equal to 0")
}
if err := s.Git.Validate(); err != nil {
return err
}
for _, r := range s.ChartRepositories {
if err := r.Validate(); err != nil {
return err
Expand Down Expand Up @@ -326,6 +329,9 @@ type PipedGit struct {
SSHKeyFile string `json:"sshKeyFile,omitempty"`
// Base64 encoded string of ssh-key.
SSHKeyData string `json:"sshKeyData,omitempty"`
// Base64 encoded string of password.
// This will be used to clone the source repo with https basic auth.
Password string `json:"password,omitempty"`
}

func (g PipedGit) ShouldConfigureSSHConfig() bool {
Expand All @@ -345,6 +351,21 @@ func (g PipedGit) LoadSSHKey() ([]byte, error) {
return nil, errors.New("either sshKeyFile or sshKeyData must be set")
}

func (g *PipedGit) Validate() error {
isPassword := g.Password != ""
isSSH := g.ShouldConfigureSSHConfig()
if isSSH && isPassword {
return errors.New("cannot configure both sshKeyData or sshKeyFile and password authentication")
}
if isSSH && (g.SSHKeyData != "" && g.SSHKeyFile != "") {
return errors.New("only either sshKeyFile or sshKeyData can be set")
}
if isPassword && (g.Username == "" || g.Password == "") {
return errors.New("both username and password must be set")
}
return nil
}

func (g *PipedGit) Mask() {
if len(g.SSHConfigFilePath) != 0 {
g.SSHConfigFilePath = maskString
Expand All @@ -355,6 +376,20 @@ func (g *PipedGit) Mask() {
if len(g.SSHKeyData) != 0 {
g.SSHKeyData = maskString
}
if len(g.Password) != 0 {
g.Password = maskString
}
}

func (g *PipedGit) DecodedPassword() (string, error) {
if len(g.Password) == 0 {
return "", nil
}
decoded, err := base64.StdEncoding.DecodeString(g.Password)
if err != nil {
return "", err
}
return string(decoded), nil
}

type PipedRepository struct {
Expand Down
83 changes: 83 additions & 0 deletions pkg/config/piped_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package config

import (
"errors"
"testing"
"time"

Expand Down Expand Up @@ -607,6 +608,7 @@ func TestPipedConfigMask(t *testing.T) {
HostName: "foo",
SSHKeyFile: "foo",
SSHKeyData: "foo",
Password: "foo",
},
Repositories: []PipedRepository{
{
Expand Down Expand Up @@ -770,6 +772,7 @@ func TestPipedConfigMask(t *testing.T) {
HostName: "foo",
SSHKeyFile: maskString,
SSHKeyData: maskString,
Password: maskString,
},
Repositories: []PipedRepository{
{
Expand Down Expand Up @@ -948,6 +951,7 @@ func TestPipedSpecClone(t *testing.T) {
Username: "username",
Email: "username@email.com",
SSHKeyFile: "/etc/piped-secret/ssh-key",
Password: "Password",
},
Repositories: []PipedRepository{
{
Expand Down Expand Up @@ -1145,6 +1149,7 @@ func TestPipedSpecClone(t *testing.T) {
Username: "username",
Email: "username@email.com",
SSHKeyFile: "/etc/piped-secret/ssh-key",
Password: "Password",
},
Repositories: []PipedRepository{
{
Expand Down Expand Up @@ -1435,3 +1440,81 @@ func TestFindPlatformProvidersByLabel(t *testing.T) {
})
}
}

func TestPipeGitValidate(t *testing.T) {
t.Parallel()
testcases := []struct {
name string
git PipedGit
err error
}{
{
name: "Both SSH and Password are not valid",
git: PipedGit{
SSHKeyData: "sshkey1",
Password: "Password",
},
err: errors.New("cannot configure both sshKeyData or sshKeyFile and password authentication"),
},
{
name: "Both SSH and Password is not valid",
git: PipedGit{
SSHKeyFile: "sshkeyfile",
SSHKeyData: "sshkeydata",
Password: "Password",
},
err: errors.New("cannot configure both sshKeyData or sshKeyFile and password authentication"),
},
{
name: "SSH key data is not empty",
git: PipedGit{
SSHKeyData: "sshkey2",
},
err: nil,
},
{
name: "SSH key file is not empty",
git: PipedGit{
SSHKeyFile: "sshkey2",
},
err: nil,
},
{
name: "Both SSH file and data is not empty",
git: PipedGit{
SSHKeyData: "sshkeydata",
SSHKeyFile: "sshkeyfile",
},
err: errors.New("only either sshKeyFile or sshKeyData can be set"),
},
{
name: "Password is valid",
git: PipedGit{
Username: "Username",
Password: "Password",
},
err: nil,
},
{
name: "Username is empty",
git: PipedGit{
Username: "",
Password: "Password",
},
err: errors.New("both username and password must be set"),
},
{
name: "Git config is empty",
git: PipedGit{},
err: nil,
},
}
for _, tc := range testcases {
tc := tc
t.Run(tc.git.SSHKeyData, func(t *testing.T) {
t.Parallel()
err := tc.git.Validate()
assert.Equal(t, tc.err, err)
})
}
}
Loading

0 comments on commit 2f57e0a

Please sign in to comment.