forked from devfile/devworkspace-operator
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathglobal.go
More file actions
151 lines (140 loc) · 4.75 KB
/
global.go
File metadata and controls
151 lines (140 loc) · 4.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
//
// Copyright (c) 2019-2026 Red Hat, 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
//
// http://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 internal
import (
"errors"
"fmt"
"log"
"os"
"regexp"
"strconv"
"time"
"github.com/devfile/devworkspace-operator/pkg/library/constants"
gittransport "github.com/go-git/go-git/v5/plumbing/transport"
githttp "github.com/go-git/go-git/v5/plumbing/transport/http"
gitssh "github.com/go-git/go-git/v5/plumbing/transport/ssh"
"github.com/kevinburke/ssh_config"
)
const (
credentialsMountPath = "/.git-credentials/credentials"
sshConfigMountPath = "/etc/ssh/ssh_config"
publicCertsDir = "/public-certs"
cloneRetriesEnvVar = "PROJECT_CLONE_RETRIES"
defaultCloneRetries = 3
maxCloneRetries = 10
BaseRetryDelay = 1 * time.Second
)
var (
ProjectsRoot string
CloneTmpDir string
CloneRetries int
tokenAuthMethod map[string]*githttp.BasicAuth
credentialsRegex = regexp.MustCompile(`https://(.+):(.+)@(.+)`)
)
// Read and store ProjectsRoot env var for reuse throughout project-clone.
func init() {
ProjectsRoot = os.Getenv(constants.ProjectsRootEnvVar)
if ProjectsRoot == "" {
log.Printf("Required environment variable %s is unset", constants.ProjectsRootEnvVar)
os.Exit(1)
}
// Have to use path within PROJECTS_ROOT in case it is a mounted directory; otherwise, moving files will fail
// (os.Rename fails when source and dest are on different partitions)
tmpDir, err := os.MkdirTemp(ProjectsRoot, "project-clone-")
if err != nil {
log.Printf("Failed to get temporary directory for setting up projects: %s", err)
os.Exit(1)
}
log.Printf("Using temporary directory %s", tmpDir)
CloneTmpDir = tmpDir
CloneRetries = defaultCloneRetries
if val := os.Getenv(cloneRetriesEnvVar); val != "" {
parsed, err := strconv.Atoi(val)
if err != nil || parsed < 0 {
log.Printf("Invalid value for %s: %q, using default (%d)", cloneRetriesEnvVar, val, defaultCloneRetries)
} else if parsed > maxCloneRetries {
log.Printf("Value for %s (%d) exceeds maximum (%d), using maximum", cloneRetriesEnvVar, parsed, maxCloneRetries)
CloneRetries = maxCloneRetries
} else {
CloneRetries = parsed
}
}
setupAuth()
}
func GetAuthForHost(repoURLStr string) (gittransport.AuthMethod, error) {
endpoint, err := gittransport.NewEndpoint(repoURLStr)
if err != nil {
return nil, fmt.Errorf("failed to parse url: %w", err)
}
switch endpoint.Protocol {
case "ssh":
identityFiles := ssh_config.GetAll(endpoint.Host, "IdentityFile")
if len(identityFiles) == 0 {
log.Printf("No SSH key found for host %s", endpoint.Host)
} else if len(identityFiles) > 1 {
// Probably should try all keys, one by one, in the future
log.Printf("Warning: multiple SSH keys found for host %s. Using first match.", endpoint.Host)
}
user := ssh_config.Get(endpoint.Host, "User")
if user == "" {
user = "git"
}
pubkeys, err := gitssh.NewPublicKeysFromFile(user, identityFiles[0], "")
if err != nil {
return nil, fmt.Errorf("failed to set up SSH: %w", err)
}
return pubkeys, nil
case "http", "https":
authMethod, ok := tokenAuthMethod[endpoint.Host]
if !ok {
log.Printf("No personal access token found for URL %s", repoURLStr)
return nil, nil
}
log.Printf("Found personal access token for URL %s", repoURLStr)
return authMethod, nil
default:
log.Printf("No personal access token for URL %s; unsupported protocol: %s", repoURLStr, endpoint.Protocol)
}
return nil, nil
}
func setupAuth() {
gitCredentials, err := os.ReadFile(credentialsMountPath)
if err != nil {
// If file does not exist, no credentials to mount
if !errors.Is(err, os.ErrNotExist) {
log.Printf("Unexpected error reading git credentials file: %s", err)
os.Exit(1)
}
} else {
tokenAuthMethod = parseCredentialsFile(string(gitCredentials))
}
}
func parseCredentialsFile(gitCredentials string) map[string]*githttp.BasicAuth {
result := map[string]*githttp.BasicAuth{}
matches := credentialsRegex.FindAllStringSubmatch(gitCredentials, -1)
for idx, credential := range matches {
if len(credential) != 4 {
log.Printf("Malformed credential found in credentials file on line %d. Skipping", idx+1)
}
username := credential[1]
pat := credential[2]
url := credential[3]
result[url] = &githttp.BasicAuth{
Username: username,
Password: pat,
}
}
return result
}