Skip to content

Commit 7352bce

Browse files
authored
Merge pull request #34 from fluxcd/gitrepository-tests
2 parents 96a76c2 + 24b77d3 commit 7352bce

File tree

7 files changed

+405
-7
lines changed

7 files changed

+405
-7
lines changed
Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
/*
2+
Copyright 2020 The Flux CD contributors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package controllers
18+
19+
import (
20+
"context"
21+
"fmt"
22+
"net/url"
23+
"os"
24+
"path"
25+
"strings"
26+
"time"
27+
28+
"github.com/go-git/go-billy/v5/memfs"
29+
"github.com/go-git/go-git/v5"
30+
"github.com/go-git/go-git/v5/config"
31+
"github.com/go-git/go-git/v5/plumbing"
32+
"github.com/go-git/go-git/v5/plumbing/object"
33+
"github.com/go-git/go-git/v5/storage/memory"
34+
. "github.com/onsi/ginkgo"
35+
. "github.com/onsi/ginkgo/extensions/table"
36+
. "github.com/onsi/gomega"
37+
corev1 "k8s.io/api/core/v1"
38+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
39+
"k8s.io/apimachinery/pkg/types"
40+
41+
sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1"
42+
"github.com/fluxcd/source-controller/internal/testserver"
43+
)
44+
45+
var _ = Describe("GitRepositoryReconciler", func() {
46+
47+
const (
48+
timeout = time.Second * 30
49+
interval = time.Second * 1
50+
indexInterval = time.Second * 1
51+
)
52+
53+
Context("GitRepository", func() {
54+
var (
55+
namespace *corev1.Namespace
56+
gitServer *testserver.GitServer
57+
err error
58+
)
59+
60+
BeforeEach(func() {
61+
namespace = &corev1.Namespace{
62+
ObjectMeta: metav1.ObjectMeta{Name: "git-repository-test" + randStringRunes(5)},
63+
}
64+
err = k8sClient.Create(context.Background(), namespace)
65+
Expect(err).NotTo(HaveOccurred(), "failed to create test namespace")
66+
67+
gitServer, err = testserver.NewTempGitServer()
68+
Expect(err).NotTo(HaveOccurred())
69+
gitServer.AutoCreate()
70+
})
71+
72+
AfterEach(func() {
73+
os.RemoveAll(gitServer.Root())
74+
75+
err = k8sClient.Delete(context.Background(), namespace)
76+
Expect(err).NotTo(HaveOccurred(), "failed to delete test namespace")
77+
})
78+
79+
type refTestCase struct {
80+
reference *sourcev1.GitRepositoryRef
81+
createRefs []string
82+
83+
waitForReason string
84+
85+
expectStatus corev1.ConditionStatus
86+
expectMessage string
87+
expectRevision string
88+
}
89+
90+
DescribeTable("Git references tests", func(t refTestCase) {
91+
err = gitServer.StartHTTP()
92+
defer gitServer.StopHTTP()
93+
Expect(err).NotTo(HaveOccurred())
94+
95+
u, err := url.Parse(gitServer.HTTPAddress())
96+
Expect(err).NotTo(HaveOccurred())
97+
u.Path = path.Join(u.Path, fmt.Sprintf("repository-%s.git", randStringRunes(5)))
98+
99+
fs := memfs.New()
100+
gitrepo, err := git.Init(memory.NewStorage(), fs)
101+
Expect(err).NotTo(HaveOccurred())
102+
103+
wt, err := gitrepo.Worktree()
104+
Expect(err).NotTo(HaveOccurred())
105+
106+
ff, _ := fs.Create("fixture")
107+
_ = ff.Close()
108+
_, err = wt.Add(fs.Join("fixture"))
109+
Expect(err).NotTo(HaveOccurred())
110+
111+
commit, err := wt.Commit("Sample", &git.CommitOptions{Author: &object.Signature{
112+
Name: "John Doe",
113+
114+
When: time.Now(),
115+
}})
116+
Expect(err).NotTo(HaveOccurred())
117+
118+
gitrepo.Worktree()
119+
120+
for _, ref := range t.createRefs {
121+
hRef := plumbing.NewHashReference(plumbing.ReferenceName(ref), commit)
122+
err = gitrepo.Storer.SetReference(hRef)
123+
Expect(err).NotTo(HaveOccurred())
124+
}
125+
126+
remote, err := gitrepo.CreateRemote(&config.RemoteConfig{
127+
Name: "origin",
128+
URLs: []string{u.String()},
129+
})
130+
Expect(err).NotTo(HaveOccurred())
131+
132+
err = remote.Push(&git.PushOptions{
133+
RefSpecs: []config.RefSpec{"refs/heads/*:refs/heads/*", "refs/tags/*:refs/tags/*"},
134+
})
135+
Expect(err).NotTo(HaveOccurred())
136+
137+
t.reference.Commit = strings.Replace(t.reference.Commit, "<commit>", commit.String(), 1)
138+
139+
key := types.NamespacedName{
140+
Name: fmt.Sprintf("git-ref-test-%s", randStringRunes(5)),
141+
Namespace: namespace.Name,
142+
}
143+
created := &sourcev1.GitRepository{
144+
ObjectMeta: metav1.ObjectMeta{
145+
Name: key.Name,
146+
Namespace: key.Namespace,
147+
},
148+
Spec: sourcev1.GitRepositorySpec{
149+
URL: u.String(),
150+
Interval: metav1.Duration{Duration: indexInterval},
151+
Reference: t.reference,
152+
},
153+
}
154+
Expect(k8sClient.Create(context.Background(), created)).Should(Succeed())
155+
defer k8sClient.Delete(context.Background(), created)
156+
157+
got := &sourcev1.GitRepository{}
158+
var cond sourcev1.SourceCondition
159+
Eventually(func() bool {
160+
_ = k8sClient.Get(context.Background(), key, got)
161+
for _, c := range got.Status.Conditions {
162+
if c.Reason == t.waitForReason {
163+
cond = c
164+
return true
165+
}
166+
}
167+
return false
168+
}, timeout, interval).Should(BeTrue())
169+
170+
Expect(cond.Status).To(Equal(t.expectStatus))
171+
Expect(cond.Message).To(ContainSubstring(t.expectMessage))
172+
Expect(got.Status.Artifact == nil).To(Equal(t.expectRevision == ""))
173+
if t.expectRevision != "" {
174+
Expect(got.Status.Artifact.Revision).To(Equal(t.expectRevision + "/" + commit.String()))
175+
}
176+
},
177+
Entry("branch", refTestCase{
178+
reference: &sourcev1.GitRepositoryRef{Branch: "some-branch"},
179+
createRefs: []string{"refs/heads/some-branch"},
180+
waitForReason: sourcev1.GitOperationSucceedReason,
181+
expectStatus: corev1.ConditionTrue,
182+
expectRevision: "some-branch",
183+
}),
184+
Entry("branch non existing", refTestCase{
185+
reference: &sourcev1.GitRepositoryRef{Branch: "invalid-branch"},
186+
waitForReason: sourcev1.GitOperationFailedReason,
187+
expectStatus: corev1.ConditionFalse,
188+
expectMessage: "couldn't find remote ref",
189+
}),
190+
Entry("tag", refTestCase{
191+
reference: &sourcev1.GitRepositoryRef{Tag: "some-tag"},
192+
createRefs: []string{"refs/tags/some-tag"},
193+
waitForReason: sourcev1.GitOperationSucceedReason,
194+
expectStatus: corev1.ConditionTrue,
195+
expectRevision: "some-tag",
196+
}),
197+
Entry("tag non existing", refTestCase{
198+
reference: &sourcev1.GitRepositoryRef{Tag: "invalid-tag"},
199+
waitForReason: sourcev1.GitOperationFailedReason,
200+
expectStatus: corev1.ConditionFalse,
201+
expectMessage: "couldn't find remote ref",
202+
}),
203+
Entry("semver", refTestCase{
204+
reference: &sourcev1.GitRepositoryRef{SemVer: "1.0.0"},
205+
createRefs: []string{"refs/tags/v1.0.0"},
206+
waitForReason: sourcev1.GitOperationSucceedReason,
207+
expectStatus: corev1.ConditionTrue,
208+
expectRevision: "v1.0.0",
209+
}),
210+
Entry("semver range", refTestCase{
211+
reference: &sourcev1.GitRepositoryRef{SemVer: ">=0.1.0 <1.0.0"},
212+
createRefs: []string{"refs/tags/0.1.0", "refs/tags/0.1.1", "refs/tags/0.2.0", "refs/tags/1.0.0"},
213+
waitForReason: sourcev1.GitOperationSucceedReason,
214+
expectStatus: corev1.ConditionTrue,
215+
expectRevision: "0.2.0",
216+
}),
217+
Entry("semver invalid", refTestCase{
218+
reference: &sourcev1.GitRepositoryRef{SemVer: "v1.0.0"},
219+
waitForReason: sourcev1.GitOperationFailedReason,
220+
expectStatus: corev1.ConditionFalse,
221+
expectMessage: "semver parse range error",
222+
}),
223+
Entry("semver no match", refTestCase{
224+
reference: &sourcev1.GitRepositoryRef{SemVer: "1.0.0"},
225+
waitForReason: sourcev1.GitOperationFailedReason,
226+
expectStatus: corev1.ConditionFalse,
227+
expectMessage: "no match found for semver: 1.0.0",
228+
}),
229+
Entry("commit", refTestCase{
230+
reference: &sourcev1.GitRepositoryRef{
231+
Commit: "<commit>",
232+
},
233+
waitForReason: sourcev1.GitOperationSucceedReason,
234+
expectStatus: corev1.ConditionTrue,
235+
expectRevision: "master",
236+
}),
237+
Entry("commit in branch", refTestCase{
238+
reference: &sourcev1.GitRepositoryRef{
239+
Branch: "some-branch",
240+
Commit: "<commit>",
241+
},
242+
createRefs: []string{"refs/heads/some-branch"},
243+
waitForReason: sourcev1.GitOperationSucceedReason,
244+
expectStatus: corev1.ConditionTrue,
245+
expectRevision: "some-branch",
246+
}),
247+
Entry("invalid commit", refTestCase{
248+
reference: &sourcev1.GitRepositoryRef{
249+
Branch: "master",
250+
Commit: "invalid",
251+
},
252+
waitForReason: sourcev1.GitOperationFailedReason,
253+
expectStatus: corev1.ConditionFalse,
254+
expectMessage: "git commit 'invalid' not found: object not found",
255+
}),
256+
)
257+
})
258+
})

controllers/storage.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ func (s *Storage) RemoveAll(artifact sourcev1.Artifact) error {
9494
// RemoveAllButCurrent removes all files for the given artifact base dir excluding the current one
9595
func (s *Storage) RemoveAllButCurrent(artifact sourcev1.Artifact) error {
9696
dir := filepath.Dir(artifact.Path)
97-
errors := []string{}
97+
var errors []string
9898
_ = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
9999
if path != artifact.Path && !info.IsDir() && info.Mode()&os.ModeSymlink != os.ModeSymlink {
100100
if err := os.Remove(path); err != nil {

controllers/suite_test.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ var _ = BeforeSuite(func(done Done) {
9494

9595
Expect(loadExampleKeys()).To(Succeed())
9696

97-
tmpStoragePath, err := ioutil.TempDir("", "helmrepository")
97+
tmpStoragePath, err := ioutil.TempDir("", "source-controller-storage-")
9898
Expect(err).NotTo(HaveOccurred(), "failed to create tmp storage dir")
9999

100100
storage, err = NewStorage(tmpStoragePath, "localhost", time.Second*30)
@@ -105,6 +105,14 @@ var _ = BeforeSuite(func(done Done) {
105105
})
106106
Expect(err).ToNot(HaveOccurred())
107107

108+
err = (&GitRepositoryReconciler{
109+
Client: k8sManager.GetClient(),
110+
Log: ctrl.Log.WithName("controllers").WithName("GitRepository"),
111+
Scheme: scheme.Scheme,
112+
Storage: storage,
113+
}).SetupWithManager(k8sManager)
114+
Expect(err).ToNot(HaveOccurred(), "failed to setup GtRepositoryReconciler")
115+
108116
err = (&HelmRepositoryReconciler{
109117
Client: k8sManager.GetClient(),
110118
Log: ctrl.Log.WithName("controllers").WithName("HelmRepository"),

go.mod

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ go 1.13
44

55
require (
66
github.com/blang/semver v3.5.0+incompatible
7+
github.com/go-git/go-billy/v5 v5.0.0
78
github.com/go-git/go-git/v5 v5.0.0
89
github.com/go-logr/logr v0.1.0
910
github.com/onsi/ginkgo v1.11.0
1011
github.com/onsi/gomega v1.8.1
12+
github.com/sosedoff/gitkit v0.2.1-0.20191202022816-7182d43c6254
1113
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073
1214
helm.sh/helm/v3 v3.1.2
1315
k8s.io/api v0.17.2
@@ -17,3 +19,7 @@ require (
1719
sigs.k8s.io/controller-runtime v0.5.0
1820
sigs.k8s.io/yaml v1.1.0
1921
)
22+
23+
// TODO(hidde): drop when PR is accepted:
24+
// https://github.com/sosedoff/gitkit/pull/21
25+
replace github.com/sosedoff/gitkit => github.com/hiddeco/gitkit v0.2.1-0.20200422093229-4355fec70348

go.sum

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,8 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
301301
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
302302
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
303303
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
304+
github.com/hiddeco/gitkit v0.2.1-0.20200422093229-4355fec70348 h1:m5YZOS7599S/K4swKGL4cDHbo2zqNhZKU2Z1leL7vuY=
305+
github.com/hiddeco/gitkit v0.2.1-0.20200422093229-4355fec70348/go.mod h1:CrGdGLdRnoHSdSLqDVwmNbgUD5BmyapU+w3Z9r+s8xY=
304306
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
305307
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
306308
github.com/huandu/xstrings v1.2.0 h1:yPeWdRnmynF7p+lLYz0H2tthW9lqhMJrQV/U7yy4wX0=
@@ -451,6 +453,7 @@ github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uY
451453
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
452454
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
453455
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
456+
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
454457
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
455458
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
456459
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=

internal/git/checkout.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,11 @@ func CheckoutStrategyForRef(ref *sourcev1.GitRepositoryRef) CheckoutStrategy {
4343
case ref.Tag != "":
4444
return &CheckoutTag{tag: ref.Tag}
4545
case ref.Commit != "":
46-
return &CheckoutCommit{branch: ref.Branch, commit: ref.Commit}
46+
strategy := &CheckoutCommit{branch: ref.Branch, commit: ref.Commit}
47+
if strategy.branch == "" {
48+
strategy.branch = defaultBranch
49+
}
50+
return strategy
4751
case ref.Branch != "":
4852
return &CheckoutBranch{branch: ref.Branch}
4953
default:
@@ -81,7 +85,7 @@ func (c *CheckoutBranch) Checkout(ctx context.Context, path, url string, auth tr
8185
}
8286
commit, err := repo.CommitObject(head.Hash())
8387
if err != nil {
84-
return nil, "", fmt.Errorf("git commit not found: %w", err)
88+
return nil, "", fmt.Errorf("git commit '%s' not found: %w", head.Hash(), err)
8589
}
8690
return commit, fmt.Sprintf("%s/%s", c.branch, head.Hash().String()), nil
8791
}
@@ -112,7 +116,7 @@ func (c *CheckoutTag) Checkout(ctx context.Context, path, url string, auth trans
112116
}
113117
commit, err := repo.CommitObject(head.Hash())
114118
if err != nil {
115-
return nil, "", fmt.Errorf("git commit not found: %w", err)
119+
return nil, "", fmt.Errorf("git commit '%s' not found: %w", head.Hash(), err)
116120
}
117121
return commit, fmt.Sprintf("%s/%s", c.tag, head.Hash().String()), nil
118122
}
@@ -143,7 +147,7 @@ func (c *CheckoutCommit) Checkout(ctx context.Context, path, url string, auth tr
143147
}
144148
commit, err := repo.CommitObject(plumbing.NewHash(c.commit))
145149
if err != nil {
146-
return nil, "", fmt.Errorf("git commit not found: %w", err)
150+
return nil, "", fmt.Errorf("git commit '%s' not found: %w", c.commit, err)
147151
}
148152
err = w.Checkout(&git.CheckoutOptions{
149153
Hash: commit.Hash,
@@ -217,7 +221,7 @@ func (c *CheckoutSemVer) Checkout(ctx context.Context, path, url string, auth tr
217221

218222
commit, err := repo.CommitObject(plumbing.NewHash(commitRef))
219223
if err != nil {
220-
return nil, "", fmt.Errorf("git commit not found: %w", err)
224+
return nil, "", fmt.Errorf("git commit '%s' not found: %w", commitRef, err)
221225
}
222226
err = w.Checkout(&git.CheckoutOptions{
223227
Hash: commit.Hash,

0 commit comments

Comments
 (0)