@@ -22,8 +22,10 @@ import (
2222 "fmt"
2323 "io/ioutil"
2424 "math/rand"
25+ "net/url"
2526 "os"
2627 "path/filepath"
28+ "strings"
2729 "time"
2830
2931 "github.com/go-git/go-billy/v5/memfs"
@@ -46,6 +48,7 @@ import (
4648 imagev1_reflect "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
4749 "github.com/fluxcd/pkg/apis/meta"
4850 "github.com/fluxcd/pkg/gittestserver"
51+ "github.com/fluxcd/pkg/ssh"
4952 sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
5053
5154 imagev1 "github.com/fluxcd/image-automation-controller/api/v1alpha1"
@@ -69,12 +72,11 @@ func randStringRunes(n int) string {
6972
7073var _ = Describe ("ImageUpdateAutomation" , func () {
7174 var (
72- impl string
73- branch string
74- repositoryPath string
75- repoURL string
76- namespace * corev1.Namespace
77- gitServer * gittestserver.GitServer
75+ branch string
76+ repositoryPath string
77+ namespace * corev1.Namespace
78+ username , password string
79+ gitServer * gittestserver.GitServer
7880 )
7981
8082 // Start the git server
@@ -89,10 +91,15 @@ var _ = Describe("ImageUpdateAutomation", func() {
8991 var err error
9092 gitServer , err = gittestserver .NewTempGitServer ()
9193 Expect (err ).NotTo (HaveOccurred ())
94+ username = randStringRunes (5 )
95+ password = randStringRunes (5 )
96+ // using authentication makes using the server more fiddly in
97+ // general, but is required for testing SSH.
98+ gitServer .Auth (username , password )
9299 gitServer .AutoCreate ()
93100 Expect (gitServer .StartHTTP ()).To (Succeed ())
94-
95- repoURL = gitServer .HTTPAddress () + repositoryPath
101+ gitServer . KeyDir ( filepath . Join ( gitServer . Root (), "keys" ))
102+ Expect ( gitServer .ListenSSH ()). To ( Succeed ())
96103 })
97104
98105 AfterEach (func () {
@@ -104,8 +111,20 @@ var _ = Describe("ImageUpdateAutomation", func() {
104111 Expect (initGitRepo (gitServer , "testdata/appconfig" , branch , repositoryPath )).To (Succeed ())
105112 })
106113
114+ // These are used for end-to-end tests; withImagePolicy is
115+ // effectively parameterised on these two values.
116+ var (
117+ // set the proto and impl in BeforeEach
118+ proto string
119+ impl string
120+ )
121+
107122 withImagePolicy := func () {
108123 var (
124+ // for cloning locally
125+ cloneLocalRepoURL string
126+ // for the controller
127+ repoURL string
109128 localRepo * git.Repository
110129 policy * imagev1_reflect.ImagePolicy
111130 policyKey types.NamespacedName
@@ -117,13 +136,31 @@ var _ = Describe("ImageUpdateAutomation", func() {
117136 const evenLatestImage = "helloworld:1.2.0"
118137
119138 BeforeEach (func () {
139+ cloneLocalRepoURL = gitServer .HTTPAddressWithCredentials () + repositoryPath
140+ if proto == "http" {
141+ repoURL = cloneLocalRepoURL // NB not testing auth for git over HTTP
142+ } else if proto == "ssh" {
143+ sshURL := gitServer .SSHAddress ()
144+ // this is expected to use 127.0.0.1, but host key
145+ // checking usually wants a hostname, so use
146+ // "localhost".
147+ sshURL = strings .Replace (sshURL , "127.0.0.1" , "localhost" , 1 )
148+ repoURL = sshURL + repositoryPath
149+ go func () {
150+ defer GinkgoRecover ()
151+ gitServer .StartSSH ()
152+ }()
153+ } else {
154+ Fail ("proto not set to http or ssh" )
155+ }
156+
120157 commitMessage = "Commit a difference " + randStringRunes (5 )
121158
122159 Expect (initGitRepo (gitServer , "testdata/appconfig" , branch , repositoryPath )).To (Succeed ())
123160
124161 var err error
125162 localRepo , err = git .Clone (memory .NewStorage (), memfs .New (), & git.CloneOptions {
126- URL : repoURL ,
163+ URL : cloneLocalRepoURL ,
127164 RemoteName : "origin" ,
128165 ReferenceName : plumbing .NewBranchReferenceName (branch ),
129166 })
@@ -145,6 +182,31 @@ var _ = Describe("ImageUpdateAutomation", func() {
145182 GitImplementation : impl ,
146183 },
147184 }
185+
186+ // If using SSH, we need to provide an identity (private
187+ // key) and known_hosts file in a secret.
188+ if proto == "ssh" {
189+ url , err := url .Parse (repoURL )
190+ Expect (err ).ToNot (HaveOccurred ())
191+ knownhosts , err := ssh .ScanHostKey (url .Host , 5 * time .Second )
192+ Expect (err ).ToNot (HaveOccurred ())
193+ keygen := ssh .NewRSAGenerator (2048 )
194+ pair , err := keygen .Generate ()
195+ Expect (err ).ToNot (HaveOccurred ())
196+
197+ sec := & corev1.Secret {
198+ StringData : map [string ]string {
199+ "known_hosts" : string (knownhosts ),
200+ "identity" : string (pair .PrivateKey ),
201+ "identity.pub" : string (pair .PublicKey ),
202+ },
203+ }
204+ sec .Name = "git-secret-" + randStringRunes (5 )
205+ sec .Namespace = namespace .Name
206+ Expect (k8sClient .Create (context .Background (), sec )).To (Succeed ())
207+ gitRepo .Spec .SecretRef = & meta.LocalObjectReference {Name : sec .Name }
208+ }
209+
148210 Expect (k8sClient .Create (context .Background (), gitRepo )).To (Succeed ())
149211
150212 policyKey = types.NamespacedName {
@@ -180,6 +242,7 @@ var _ = Describe("ImageUpdateAutomation", func() {
180242 AfterEach (func () {
181243 Expect (k8sClient .Delete (context .Background (), namespace )).To (Succeed ())
182244 Expect (k8sClient .Delete (context .Background (), policy )).To (Succeed ())
245+ Expect (gitServer .StopSSH ()).To (Succeed ())
183246 })
184247
185248 Context ("defaulting" , func () {
@@ -234,7 +297,7 @@ var _ = Describe("ImageUpdateAutomation", func() {
234297 BeforeEach (func () {
235298 // Insert a setter reference into the deployment file,
236299 // before creating the automation object itself.
237- commitInRepo (repoURL , branch , "Install setter marker" , func (tmp string ) {
300+ commitInRepo (cloneLocalRepoURL , branch , "Install setter marker" , func (tmp string ) {
238301 replaceMarker (tmp , policyKey )
239302 })
240303
@@ -291,7 +354,7 @@ var _ = Describe("ImageUpdateAutomation", func() {
291354 Expect (newObj .Status .LastPushCommit ).To (Equal (head .Hash ().String ()))
292355 Expect (newObj .Status .LastPushTime ).ToNot (BeNil ())
293356
294- compareRepoWithExpected (repoURL , branch , "testdata/appconfig-setters-expected" , func (tmp string ) {
357+ compareRepoWithExpected (cloneLocalRepoURL , branch , "testdata/appconfig-setters-expected" , func (tmp string ) {
295358 replaceMarker (tmp , policyKey )
296359 })
297360 })
@@ -338,21 +401,35 @@ var _ = Describe("ImageUpdateAutomation", func() {
338401 }
339402
340403 Context ("Using go-git" , func () {
341- BeforeEach (func () {
342- impl = sourcev1 .GoGitImplementation
404+ BeforeEach (func () { impl = sourcev1 .GoGitImplementation })
405+
406+ Context ("with HTTP" , func () {
407+ BeforeEach (func () { proto = "http" })
408+ Describe ("with image policy" , withImagePolicy )
343409 })
344410
345- Context ("with image policy" , withImagePolicy )
411+ Context ("with SSH" , func () {
412+ BeforeEach (func () { proto = "ssh" })
413+ Describe ("with image policy" , withImagePolicy )
414+ })
346415 })
347416
348417 Context ("Using libgit2" , func () {
349- BeforeEach (func () {
350- impl = sourcev1 .LibGit2Implementation
418+ BeforeEach (func () { impl = sourcev1 .LibGit2Implementation })
419+
420+ Context ("with HTTP" , func () {
421+ BeforeEach (func () { proto = "http" })
422+ Describe ("with image policy" , withImagePolicy )
351423 })
352424
353- Context ("with image policy" , withImagePolicy )
425+ // Marked "Pending" because the libgit2 SSH implementation
426+ // won't work with the gittestserver yet -- see
427+ // https://github.com/fluxcd/source-controller/issues/287
428+ Context ("with SSH" , func () {
429+ BeforeEach (func () { proto = "ssh" })
430+ Describe ("with image policy" , withImagePolicy )
431+ })
354432 })
355-
356433})
357434
358435func expectCommittedAndPushed (conditions []metav1.Condition ) {
@@ -498,7 +575,7 @@ func initGitRepo(gitServer *gittestserver.GitServer, fixture, branch, repository
498575
499576 remote , err := repo .CreateRemote (& config.RemoteConfig {
500577 Name : "origin" ,
501- URLs : []string {gitServer .HTTPAddress () + repositoryPath },
578+ URLs : []string {gitServer .HTTPAddressWithCredentials () + repositoryPath },
502579 })
503580 if err != nil {
504581 return err
0 commit comments