Skip to content

Commit acec1dd

Browse files
committed
Add option to specify Git credentials for private Git repositories
1 parent 4fb5f62 commit acec1dd

8 files changed

Lines changed: 130 additions & 25 deletions

File tree

README.md

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ metadata:
4646
spec:
4747
source:
4848
repositoryUrl: https://github.com/your-org/your-function.git
49+
authSecretRef:
50+
name: git-credentials
4951
registry:
5052
path: quay.io/your-username/my-function
5153
authSecretRef:
@@ -83,6 +85,48 @@ kubectl create secret docker-registry registry-credentials \
8385
--docker-email=<email>
8486
```
8587

88+
### Git Authentication
89+
90+
For private Git repositories, create a secret with the Git credentials:
91+
92+
```yaml
93+
apiVersion: v1
94+
kind: Secret
95+
metadata:
96+
name: git-credentials
97+
namespace: default
98+
data:
99+
token: <base64-encoded-access-token>
100+
```
101+
102+
or
103+
104+
```yaml
105+
apiVersion: v1
106+
kind: Secret
107+
metadata:
108+
name: git-credentials
109+
namespace: default
110+
data:
111+
username: <base64-encoded-username>
112+
password: <base64-encoded-password>
113+
```
114+
115+
Then reference it in the Function under `.spec.source.authSecretRef.name`
116+
117+
```yaml
118+
apiVersion: functions.dev/v1alpha1
119+
kind: Function
120+
metadata:
121+
name: my-function
122+
namespace: default
123+
spec:
124+
source:
125+
repositoryUrl: https://github.com/your-org/your-function.git
126+
authSecretRef:
127+
name: git-credentials
128+
```
129+
86130
### Check Function Status
87131

88132
View the status of your function:
@@ -169,12 +213,13 @@ make lint
169213

170214
### Function Spec
171215

172-
| Field | Type | Required | Description |
173-
|-------|------|----------|-------------|
174-
| `source.repositoryUrl` | string | Yes | Git repository URL containing the function source code |
175-
| `registry.path` | string | Yes | Container registry path for the function image |
176-
| `registry.insecure` | boolean | No | Allow insecure registry connections |
177-
| `registry.authSecretRef` | object | No | Reference to registry authentication secret |
216+
| Field | Type | Required | Description |
217+
|--------------------------|---------|----------|--------------------------------------------------------|
218+
| `source.repositoryUrl` | string | Yes | Git repository URL containing the function source code |
219+
| `source.authSecretRef` | object | No | Reference to Git repository authentication secret |
220+
| `registry.path` | string | Yes | Container registry path for the function image |
221+
| `registry.insecure` | boolean | No | Allow insecure registry connections |
222+
| `registry.authSecretRef` | object | No | Reference to registry authentication secret |
178223

179224
### Function Status
180225

api/v1alpha1/function_types.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ type FunctionSpecSource struct {
4646

4747
// +kubebuilder:validation:Optional
4848
Reference string `json:"reference"`
49+
50+
AuthSecretRef *v1.LocalObjectReference `json:"authSecretRef,omitempty"`
4951
}
5052

5153
type FunctionSpecRegistry struct {

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 6 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crd/bases/functions.dev_functions.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,22 @@ spec:
6767
type: object
6868
source:
6969
properties:
70+
authSecretRef:
71+
description: |-
72+
LocalObjectReference contains enough information to let you locate the
73+
referenced object inside the same namespace.
74+
properties:
75+
name:
76+
default: ""
77+
description: |-
78+
Name of the referent.
79+
This field is effectively required, but due to backwards compatibility is
80+
allowed to be empty. Instances of this type with an empty value here are
81+
almost certainly wrong.
82+
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
83+
type: string
84+
type: object
85+
x-kubernetes-map-type: atomic
7086
reference:
7187
type: string
7288
repositoryUrl:

internal/controller/function_controller.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,15 @@ func (r *FunctionReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c
9090
if function.Spec.Source.Reference != "" {
9191
branchReference = function.Spec.Source.Reference
9292
}
93-
repo, err := r.GitManager.CloneRepository(ctx, function.Spec.Source.RepositoryURL, branchReference)
93+
94+
gitAuthSecret := v1.Secret{}
95+
if function.Spec.Source.AuthSecretRef != nil {
96+
if err := r.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: function.Spec.Source.AuthSecretRef.Name}, &gitAuthSecret); err != nil {
97+
return ctrl.Result{}, err
98+
}
99+
}
100+
101+
repo, err := r.GitManager.CloneRepository(ctx, function.Spec.Source.RepositoryURL, branchReference, gitAuthSecret.Data)
94102
if err != nil {
95103
return ctrl.Result{}, fmt.Errorf("failed to setup git repository: %w", err)
96104
}

internal/controller/function_controller_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ var _ = Describe("Function Controller", func() {
128128
Builder: "s2i",
129129
}).Return(nil)
130130

131-
gitMock.EXPECT().CloneRepository(mock.Anything, "https://github.com/foo/bar", "my-branch").Return(createTmpGitRepo(functions.Function{Name: "func-go"}), nil)
131+
gitMock.EXPECT().CloneRepository(mock.Anything, "https://github.com/foo/bar", "my-branch", mock.Anything).Return(createTmpGitRepo(functions.Function{Name: "func-go"}), nil)
132132
},
133133
}),
134134
Entry("should skip deploy when middleware already up to date", reconcileTestCase{
@@ -142,7 +142,7 @@ var _ = Describe("Function Controller", func() {
142142
funcMock.EXPECT().GetLatestMiddlewareVersion(mock.Anything, mock.Anything, mock.Anything).Return("v1.0.0", nil)
143143
funcMock.EXPECT().GetMiddlewareVersion(mock.Anything, functionName, resourceNamespace).Return("v1.0.0", nil)
144144

145-
gitMock.EXPECT().CloneRepository(mock.Anything, "https://github.com/foo/bar", "my-branch").Return(createTmpGitRepo(functions.Function{Name: "func-go"}), nil)
145+
gitMock.EXPECT().CloneRepository(mock.Anything, "https://github.com/foo/bar", "my-branch", mock.Anything).Return(createTmpGitRepo(functions.Function{Name: "func-go"}), nil)
146146
},
147147
}),
148148
Entry("should use main as default branch", reconcileTestCase{
@@ -163,7 +163,7 @@ var _ = Describe("Function Controller", func() {
163163
funcMock.EXPECT().GetLatestMiddlewareVersion(mock.Anything, mock.Anything, mock.Anything).Return("v1.0.0", nil)
164164
funcMock.EXPECT().GetMiddlewareVersion(mock.Anything, functionName, resourceNamespace).Return("v1.0.0", nil)
165165

166-
gitMock.EXPECT().CloneRepository(mock.Anything, "https://github.com/foo/bar", "main").Return(createTmpGitRepo(functions.Function{Name: "func-go"}), nil)
166+
gitMock.EXPECT().CloneRepository(mock.Anything, "https://github.com/foo/bar", "main", mock.Anything).Return(createTmpGitRepo(functions.Function{Name: "func-go"}), nil)
167167
},
168168
}),
169169
)

internal/git/Manager_mock.go

Lines changed: 18 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/git/manager.go

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import (
1010
"github.com/creydr/func-operator/internal/monitoring"
1111
"github.com/go-git/go-git/v6"
1212
"github.com/go-git/go-git/v6/plumbing"
13+
"github.com/go-git/go-git/v6/plumbing/transport"
14+
"github.com/go-git/go-git/v6/plumbing/transport/http"
1315
"github.com/prometheus/client_golang/prometheus"
1416
)
1517

@@ -18,7 +20,7 @@ const (
1820
)
1921

2022
type Manager interface {
21-
CloneRepository(ctx context.Context, url, reference string) (*Repository, error)
23+
CloneRepository(ctx context.Context, url, reference string, auth map[string][]byte) (*Repository, error)
2224
}
2325

2426
func NewManager() Manager {
@@ -27,7 +29,7 @@ func NewManager() Manager {
2729

2830
type managerImpl struct{}
2931

30-
func (*managerImpl) CloneRepository(ctx context.Context, repoUrl, reference string) (*Repository, error) {
32+
func (m *managerImpl) CloneRepository(ctx context.Context, repoUrl, reference string, auth map[string][]byte) (*Repository, error) {
3133
timer := prometheus.NewTimer(monitoring.GitCloneDuration)
3234
defer timer.ObserveDuration()
3335

@@ -47,6 +49,7 @@ func (*managerImpl) CloneRepository(ctx context.Context, repoUrl, reference stri
4749
ReferenceName: plumbing.ReferenceName(reference),
4850
SingleBranch: true,
4951
Depth: 1,
52+
Auth: m.getAuth(auth),
5053
})
5154
if err != nil {
5255
return nil, fmt.Errorf("failed to clone repo: %w", err)
@@ -56,3 +59,23 @@ func (*managerImpl) CloneRepository(ctx context.Context, repoUrl, reference stri
5659
CloneDir: targetDir,
5760
}, nil
5861
}
62+
63+
func (m *managerImpl) getAuth(authSecret map[string][]byte) transport.AuthMethod {
64+
if len(authSecret) == 0 {
65+
return nil
66+
} else if token, ok := authSecret["token"]; ok {
67+
return &http.BasicAuth{
68+
Username: "empty", // can be anything except an empty string
69+
Password: string(token),
70+
}
71+
} else if username, ok := authSecret["username"]; ok {
72+
if password, ok := authSecret["password"]; ok {
73+
return &http.BasicAuth{
74+
Username: string(username),
75+
Password: string(password),
76+
}
77+
}
78+
return nil
79+
} // add other auth methods when needed
80+
return nil
81+
}

0 commit comments

Comments
 (0)