Skip to content

Commit 4d05a0f

Browse files
committed
wip: set token outside of cloning function
Signed-off-by: Michael Hoang <[email protected]>
1 parent 60fa9f5 commit 4d05a0f

File tree

7 files changed

+183
-98
lines changed

7 files changed

+183
-98
lines changed

README.md

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,24 @@ The Devfile Parser library is a Golang module that:
1818
Tokens are required to be set in the following cases:
1919
1. tooling client calling the library API
2020
2. parsing a devfile from a private repository
21-
3. parsing a devfile containing a parent devfile from a private repository
22-
23-
Set the environment variables for the necessary git providers:
24-
```shell
25-
export GITHUB_TOKEN=<account_token>
26-
export GITLAB_TOKEN=<account_token>
27-
export BITBUCKET_TOKEN=<account_token>
28-
```
21+
3. parsing a devfile containing a parent devfile from a private repository [1]
22+
23+
Set the token for the repository:
24+
```go
25+
parser.ParserArgs{
26+
...
27+
Token: <repo-personal-access-token>
28+
...
29+
}
30+
```
2931

3032
For more information about personal access tokens:
3133
1. [GitHub docs](https://docs.github.com/en/[email protected]/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token)
3234
2. [GitLab docs](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html#create-a-personal-access-token)
3335
3. [Bitbucket docs](https://support.atlassian.com/bitbucket-cloud/docs/repository-access-tokens/)
3436

37+
[1] currently, this works under the assumption that the token can authenticate the devfile and the parent devfile; both devfiles are in the same repository.
38+
3539
## Usage
3640

3741
The function documentation can be accessed via [pkg.go.dev](https://pkg.go.dev/github.com/devfile/library).

pkg/devfile/parser/parse.go

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ type ParserArgs struct {
9797
// RegistryURLs is a list of registry hosts which parser should pull parent devfile from.
9898
// If registryUrl is defined in devfile, this list will be ignored.
9999
RegistryURLs []string
100+
// Token is a GitHub, GitLab, or Bitbucket personal access token used with a private git repo uri
101+
Token string
100102
// DefaultNamespace is the default namespace to use
101103
// If namespace is defined under devfile's parent kubernetes object, this namespace will be ignored.
102104
DefaultNamespace string
@@ -132,6 +134,7 @@ func ParseDevfile(args ParserArgs) (d DevfileObj, err error) {
132134
context: args.Context,
133135
k8sClient: args.K8sClient,
134136
httpTimeout: args.HTTPTimeout,
137+
token: args.Token,
135138
}
136139

137140
flattenedDevfile := true
@@ -175,6 +178,8 @@ type resolverTools struct {
175178
// RegistryURLs is a list of registry hosts which parser should pull parent devfile from.
176179
// If registryUrl is defined in devfile, this list will be ignored.
177180
registryURLs []string
181+
// Token is a GitHub, GitLab, or Bitbucket personal access token used with a private git repo uri
182+
token string
178183
// Context is the context used for making Kubernetes or HTTP requests
179184
context context.Context
180185
// K8sClient is the Kubernetes client instance used for interacting with a cluster
@@ -425,15 +430,14 @@ func parseFromURI(importReference v1.ImportReference, curDevfileCtx devfileCtx.D
425430
}
426431

427432
d.Ctx = devfileCtx.NewURLDevfileCtx(newUri)
428-
if strings.Contains(newUri, util.RawGitHubHost) || strings.Contains(newUri, util.GitHubHost) ||
429-
strings.Contains(newUri, util.GitLabHost) || strings.Contains(newUri, util.BitbucketToken) {
433+
if util.IsGitProviderRepo(newUri) {
430434
gitUrl, err := util.ParseGitUrl(newUri)
431435
if err != nil {
432436
return DevfileObj{}, err
433437
}
434438
if gitUrl.IsFile {
435439
destDir := path.Dir(curDevfileCtx.GetAbsPath())
436-
err = getResourcesFromGit(gitUrl, destDir, tool.httpTimeout)
440+
err = getResourcesFromGit(gitUrl, destDir, tool.httpTimeout, tool.token)
437441
if err != nil {
438442
return DevfileObj{}, err
439443
}
@@ -446,13 +450,20 @@ func parseFromURI(importReference v1.ImportReference, curDevfileCtx devfileCtx.D
446450
return populateAndParseDevfile(d, newResolveCtx, tool, true)
447451
}
448452

449-
func getResourcesFromGit(g util.GitUrl, destDir string, httpTimeout *int) error {
453+
func getResourcesFromGit(g util.GitUrl, destDir string, httpTimeout *int, repoToken string) error {
450454
stackDir, err := ioutil.TempDir(os.TempDir(), fmt.Sprintf("git-resources"))
451455
if err != nil {
452456
return fmt.Errorf("failed to create dir: %s, error: %v", stackDir, err)
453457
}
454458
defer os.RemoveAll(stackDir)
455459

460+
if !g.IsPublic(httpTimeout) {
461+
err = g.SetToken(repoToken, httpTimeout)
462+
if err != nil {
463+
return err
464+
}
465+
}
466+
456467
err = util.CloneGitRepo(g, stackDir, httpTimeout)
457468
if err != nil {
458469
return err

pkg/devfile/parser/parse_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4207,7 +4207,7 @@ func Test_getResourcesFromGit(t *testing.T) {
42074207

42084208
for _, tt := range tests {
42094209
t.Run(tt.name, func(t *testing.T) {
4210-
err := getResourcesFromGit(tt.gitUrl, tt.destDir, &httpTimeout)
4210+
err := getResourcesFromGit(tt.gitUrl, tt.destDir, &httpTimeout, "")
42114211
if (err != nil) != tt.wantErr {
42124212
t.Errorf("Expected error: %t, got error: %t", tt.wantErr, err)
42134213
}

pkg/util/git.go

Lines changed: 50 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,6 @@ const (
2929
RawGitHubHost string = "raw.githubusercontent.com"
3030
GitLabHost string = "gitlab.com"
3131
BitbucketHost string = "bitbucket.org"
32-
33-
GitHubToken string = "GITHUB_TOKEN"
34-
GitLabToken string = "GITLAB_TOKEN"
35-
BitbucketToken string = "BITBUCKET_TOKEN"
3632
)
3733

3834
type GitUrl struct {
@@ -46,7 +42,8 @@ type GitUrl struct {
4642
IsFile bool // defines if the URL points to a file in the repo
4743
}
4844

49-
// ParseGitUrl extracts information from a GitHub, GitLab, or Bitbucket url
45+
// ParseGitUrl extracts information from a support git url
46+
// Only supports git repositories hosted on GitHub, GitLab, and Bitbucket
5047
func ParseGitUrl(fullUrl string) (GitUrl, error) {
5148
var g GitUrl
5249

@@ -83,7 +80,6 @@ func (g *GitUrl) parseGitHubUrl(url *url.URL) error {
8380

8481
g.Protocol = url.Scheme
8582
g.Host = url.Host
86-
g.token = os.Getenv(GitHubToken)
8783

8884
if g.Host == RawGitHubHost {
8985
g.IsFile = true
@@ -130,7 +126,6 @@ func (g *GitUrl) parseGitLabUrl(url *url.URL) error {
130126
g.Protocol = url.Scheme
131127
g.Host = url.Host
132128
g.IsFile = false
133-
g.token = os.Getenv(GitLabToken)
134129

135130
// GitLab urls contain a '-' separating the root of the repo
136131
// and the path to a file or directory
@@ -171,7 +166,6 @@ func (g *GitUrl) parseBitbucketUrl(url *url.URL) error {
171166
g.Protocol = url.Scheme
172167
g.Host = url.Host
173168
g.IsFile = false
174-
g.token = os.Getenv(BitbucketToken)
175169

176170
splitUrl = strings.SplitN(url.Path[1:], "/", 5)
177171
if len(splitUrl) < 2 {
@@ -201,10 +195,31 @@ func (g *GitUrl) parseBitbucketUrl(url *url.URL) error {
201195
return err
202196
}
203197

204-
// ValidateToken makes a http get request to the repo with the GitUrl token
198+
// SetToken validates the token with a get request to the repo before setting the token
199+
// Defaults token to empty on failure.
200+
func (g *GitUrl) SetToken(token string, httpTimeout *int) error {
201+
err := g.validateToken(HTTPRequestParams{Token: token, Timeout: httpTimeout})
202+
if err != nil {
203+
g.token = ""
204+
return fmt.Errorf("failed to set token. error: %v", err)
205+
}
206+
g.token = token
207+
return nil
208+
}
209+
210+
// IsPublic checks if the GitUrl is public with a get request to the repo using an empty token
211+
// Returns true if the request succeeds
212+
func (g *GitUrl) IsPublic(httpTimeout *int) bool {
213+
err := g.validateToken(HTTPRequestParams{Token: "", Timeout: httpTimeout})
214+
if err != nil {
215+
return false
216+
}
217+
return true
218+
}
219+
220+
// validateToken makes a http get request to the repo with the GitUrl token
205221
// Returns an error if the get request fails
206-
// If token is empty or invalid and validate succeeds, the repository is public
207-
func (g *GitUrl) ValidateToken(params HTTPRequestParams) error {
222+
func (g *GitUrl) validateToken(params HTTPRequestParams) error {
208223
var apiUrl string
209224

210225
switch g.Host {
@@ -227,9 +242,17 @@ func (g *GitUrl) ValidateToken(params HTTPRequestParams) error {
227242
return nil
228243
}
229244

230-
// CloneGitRepo clones a git repo to a destination directory
231-
// Only supports git repositories hosted on GitHub, GitLab, and Bitbucket
232-
func CloneGitRepo(g GitUrl, destDir string, httpTimeout *int) error {
245+
// IsGitProviderRepo checks if the url matches a repo from a supported git provider
246+
func IsGitProviderRepo(url string) bool {
247+
if strings.Contains(url, RawGitHubHost) || strings.Contains(url, GitHubHost) ||
248+
strings.Contains(url, GitLabHost) || strings.Contains(url, BitbucketHost) {
249+
return true
250+
}
251+
return false
252+
}
253+
254+
// CloneGitRepo clones a git repo to a destination directory (either an absolute or relative path)
255+
func CloneGitRepo(g GitUrl, destDir string) error {
233256
exist := CheckPathExists(destDir)
234257
if !exist {
235258
return fmt.Errorf("failed to clone repo, destination directory: '%s' does not exists", destDir)
@@ -240,29 +263,13 @@ func CloneGitRepo(g GitUrl, destDir string, httpTimeout *int) error {
240263
host = GitHubHost
241264
}
242265

243-
repoUrl := fmt.Sprintf("%s://%s/%s/%s.git", g.Protocol, host, g.Owner, g.Repo)
244-
245-
params := HTTPRequestParams{
246-
Timeout: httpTimeout,
247-
}
248-
249-
// public repos will succeed even if token is invalid or empty
250-
err := g.ValidateToken(params)
251-
252-
if err != nil {
253-
if g.token != "" {
254-
params.Token = g.token
255-
err := g.ValidateToken(params)
256-
if err != nil {
257-
return fmt.Errorf("failed to validate git url with token, ensure that the url and token is correct. error: %v", err)
258-
} else {
259-
repoUrl = fmt.Sprintf("%s://token:%s@%s/%s/%s.git", g.Protocol, g.token, host, g.Owner, g.Repo)
260-
if g.Host == BitbucketHost {
261-
repoUrl = fmt.Sprintf("%s://x-token-auth:%s@%s/%s/%s.git", g.Protocol, g.token, host, g.Owner, g.Repo)
262-
}
263-
}
264-
} else {
265-
return fmt.Errorf("failed to validate git url without a token, ensure that a token is set if the repo is private. error: %v", err)
266+
var repoUrl string
267+
if g.token == "" {
268+
repoUrl = fmt.Sprintf("%s://%s/%s/%s.git", g.Protocol, host, g.Owner, g.Repo)
269+
} else {
270+
repoUrl = fmt.Sprintf("%s://token:%s@%s/%s/%s.git", g.Protocol, g.token, host, g.Owner, g.Repo)
271+
if g.Host == BitbucketHost {
272+
repoUrl = fmt.Sprintf("%s://x-token-auth:%s@%s/%s/%s.git", g.Protocol, g.token, host, g.Owner, g.Repo)
266273
}
267274
}
268275

@@ -274,9 +281,13 @@ func CloneGitRepo(g GitUrl, destDir string, httpTimeout *int) error {
274281
c.Env = os.Environ()
275282
c.Env = append(c.Env, "GIT_TERMINAL_PROMPT=0", "GIT_ASKPASS=/bin/echo")
276283

277-
_, err = c.CombinedOutput()
284+
_, err := c.CombinedOutput()
278285
if err != nil {
279-
return fmt.Errorf("failed to clone repo, ensure that the git url is correct. error: %v", err)
286+
if g.token == "" {
287+
return fmt.Errorf("failed to clone repo without a token, ensure that a token is set if the repo is private. error: %v", err)
288+
} else {
289+
return fmt.Errorf("failed to clone repo with token, ensure that the url and token is correct. error: %v", err)
290+
}
280291
}
281292

282293
return nil

0 commit comments

Comments
 (0)