Skip to content

Commit a1f05d9

Browse files
authored
Merge pull request #126 from fluxcd/update-path
Allow specifying the path for manifests updates
2 parents 43d9d39 + 7c5d7b8 commit a1f05d9

File tree

11 files changed

+265
-7
lines changed

11 files changed

+265
-7
lines changed

api/v1alpha1/imageupdateautomation_types.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,18 @@ type ImageUpdateAutomationSpec struct {
3030
// ready to make changes.
3131
// +required
3232
Checkout GitCheckoutSpec `json:"checkout"`
33+
3334
// Interval gives an lower bound for how often the automation
3435
// run should be attempted.
3536
// +required
3637
Interval metav1.Duration `json:"interval"`
38+
3739
// Update gives the specification for how to update the files in
3840
// the repository. This can be left empty, to use the default
3941
// value.
4042
// +kubebuilder:default={"strategy":"Setters"}
4143
Update *UpdateStrategy `json:"update,omitempty"`
44+
4245
// Commit specifies how to commit to the git repository.
4346
// +required
4447
Commit CommitSpec `json:"commit"`
@@ -87,6 +90,12 @@ type UpdateStrategy struct {
8790
// +required
8891
// +kubebuilder:default=Setters
8992
Strategy UpdateStrategyName `json:"strategy"`
93+
94+
// Path to the directory containing the manifests to be updated.
95+
// Defaults to 'None', which translates to the root path
96+
// of the GitRepositoryRef.
97+
// +optional
98+
Path string `json:"path,omitempty"`
9099
}
91100

92101
// CommitSpec specifies how to commit changes to the git repository

config/crd/bases/image.toolkit.fluxcd.io_imageupdateautomations.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,11 @@ spec:
111111
files in the repository. This can be left empty, to use the default
112112
value.
113113
properties:
114+
path:
115+
description: Path to the directory containing the manifests to
116+
be updated. Defaults to 'None', which translates to the root
117+
path of the GitRepositoryRef.
118+
type: string
114119
strategy:
115120
default: Setters
116121
description: Strategy names the strategy to be used.

controllers/imageupdateautomation_controller.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
gogit "github.com/go-git/go-git/v5"
3131
libgit2 "github.com/libgit2/git2go/v31"
3232

33+
securejoin "github.com/cyphar/filepath-securejoin"
3334
"github.com/go-git/go-git/v5/plumbing"
3435
"github.com/go-git/go-git/v5/plumbing/object"
3536
"github.com/go-logr/logr"
@@ -196,7 +197,16 @@ func (r *ImageUpdateAutomationReconciler) Reconcile(ctx context.Context, req ctr
196197
return failWithError(err)
197198
}
198199

199-
if result, err := updateAccordingToSetters(ctx, tmp, policies.Items); err != nil {
200+
manifestsPath := tmp
201+
if auto.Spec.Update.Path != "" {
202+
if p, err := securejoin.SecureJoin(tmp, auto.Spec.Update.Path); err != nil {
203+
return failWithError(err)
204+
} else {
205+
manifestsPath = p
206+
}
207+
}
208+
209+
if result, err := updateAccordingToSetters(ctx, manifestsPath, policies.Items); err != nil {
200210
return failWithError(err)
201211
} else {
202212
templateValues.Updated = result

controllers/suite_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ var _ = BeforeSuite(func(done Done) {
9090
Expect(imageAutoReconciler.SetupWithManager(k8sManager)).To(Succeed())
9191

9292
go func() {
93+
defer GinkgoRecover()
9394
err = k8sManager.Start(ctrl.SetupSignalHandler())
9495
Expect(err).ToNot(HaveOccurred())
9596
}()
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
name: update-no
5+
spec:
6+
template:
7+
spec:
8+
containers:
9+
- name: hello
10+
image: helloworld:1.0.0 # SETTER_SITE
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
name: update-yes
5+
spec:
6+
template:
7+
spec:
8+
containers:
9+
- name: hello
10+
image: helloworld:1.0.0 # SETTER_SITE

controllers/update_test.go

Lines changed: 125 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"math/rand"
2525
"net/url"
2626
"os"
27+
"path"
2728
"path/filepath"
2829
"strings"
2930
"time"
@@ -263,6 +264,130 @@ Images:
263264
})
264265
})
265266

267+
Context("update path", func() {
268+
269+
var localRepo *git.Repository
270+
const commitTemplate = `Commit summary
271+
272+
{{ range $resource, $_ := .Updated.Objects -}}
273+
- {{ $resource.Name }}
274+
{{ end -}}
275+
`
276+
277+
BeforeEach(func() {
278+
Expect(initGitRepo(gitServer, "testdata/pathconfig", branch, repositoryPath)).To(Succeed())
279+
repoURL := gitServer.HTTPAddressWithCredentials() + repositoryPath
280+
var err error
281+
localRepo, err = git.Clone(memory.NewStorage(), memfs.New(), &git.CloneOptions{
282+
URL: repoURL,
283+
RemoteName: "origin",
284+
ReferenceName: plumbing.NewBranchReferenceName(branch),
285+
})
286+
Expect(err).ToNot(HaveOccurred())
287+
288+
gitRepoKey := types.NamespacedName{
289+
Name: "image-auto-" + randStringRunes(5),
290+
Namespace: namespace.Name,
291+
}
292+
gitRepo := &sourcev1.GitRepository{
293+
ObjectMeta: metav1.ObjectMeta{
294+
Name: gitRepoKey.Name,
295+
Namespace: namespace.Name,
296+
},
297+
Spec: sourcev1.GitRepositorySpec{
298+
URL: repoURL,
299+
Interval: metav1.Duration{Duration: time.Minute},
300+
},
301+
}
302+
Expect(k8sClient.Create(context.Background(), gitRepo)).To(Succeed())
303+
policyKey := types.NamespacedName{
304+
Name: "policy-" + randStringRunes(5),
305+
Namespace: namespace.Name,
306+
}
307+
// NB not testing the image reflector controller; this
308+
// will make a "fully formed" ImagePolicy object.
309+
policy := &imagev1_reflect.ImagePolicy{
310+
ObjectMeta: metav1.ObjectMeta{
311+
Name: policyKey.Name,
312+
Namespace: policyKey.Namespace,
313+
},
314+
Spec: imagev1_reflect.ImagePolicySpec{
315+
ImageRepositoryRef: meta.LocalObjectReference{
316+
Name: "not-expected-to-exist",
317+
},
318+
Policy: imagev1_reflect.ImagePolicyChoice{
319+
SemVer: &imagev1_reflect.SemVerPolicy{
320+
Range: "1.x",
321+
},
322+
},
323+
},
324+
Status: imagev1_reflect.ImagePolicyStatus{
325+
LatestImage: "helloworld:v1.0.0",
326+
},
327+
}
328+
Expect(k8sClient.Create(context.Background(), policy)).To(Succeed())
329+
Expect(k8sClient.Status().Update(context.Background(), policy)).To(Succeed())
330+
331+
// Insert a setter reference into the deployment file,
332+
// before creating the automation object itself.
333+
commitInRepo(repoURL, branch, "Install setter marker", func(tmp string) {
334+
replaceMarker(path.Join(tmp, "yes"), policyKey)
335+
})
336+
commitInRepo(repoURL, branch, "Install setter marker", func(tmp string) {
337+
replaceMarker(path.Join(tmp, "no"), policyKey)
338+
})
339+
340+
// pull the head commit we just pushed, so it's not
341+
// considered a new commit when checking for a commit
342+
// made by automation.
343+
waitForNewHead(localRepo, branch)
344+
345+
// now create the automation object, and let it (one
346+
// hopes!) make a commit itself.
347+
updateKey := types.NamespacedName{
348+
Namespace: namespace.Name,
349+
Name: "update-test",
350+
}
351+
updateBySetters := &imagev1.ImageUpdateAutomation{
352+
ObjectMeta: metav1.ObjectMeta{
353+
Name: updateKey.Name,
354+
Namespace: updateKey.Namespace,
355+
},
356+
Spec: imagev1.ImageUpdateAutomationSpec{
357+
Interval: metav1.Duration{Duration: 2 * time.Hour}, // this is to ensure any subsequent run should be outside the scope of the testing
358+
Checkout: imagev1.GitCheckoutSpec{
359+
GitRepositoryRef: meta.LocalObjectReference{
360+
Name: gitRepoKey.Name,
361+
},
362+
Branch: branch,
363+
},
364+
Update: &imagev1.UpdateStrategy{
365+
Strategy: imagev1.UpdateStrategySetters,
366+
Path: "./yes",
367+
},
368+
Commit: imagev1.CommitSpec{
369+
MessageTemplate: commitTemplate,
370+
},
371+
},
372+
}
373+
Expect(k8sClient.Create(context.Background(), updateBySetters)).To(Succeed())
374+
// wait for a new commit to be made by the controller
375+
waitForNewHead(localRepo, branch)
376+
})
377+
378+
AfterEach(func() {
379+
Expect(k8sClient.Delete(context.Background(), namespace)).To(Succeed())
380+
})
381+
382+
It("updates only the deployment in the specified path", func() {
383+
head, _ := localRepo.Head()
384+
commit, err := localRepo.CommitObject(head.Hash())
385+
Expect(err).ToNot(HaveOccurred())
386+
Expect(commit.Message).To(Not(ContainSubstring("update-no")))
387+
Expect(commit.Message).To(ContainSubstring("update-yes"))
388+
})
389+
})
390+
266391
endToEnd := func(impl, proto string) func() {
267392
return func() {
268393
var (
@@ -619,7 +744,6 @@ Images:
619744
Expect(fetchedAuto.Spec.Update).To(Equal(&imagev1.UpdateStrategy{Strategy: imagev1.UpdateStrategySetters}))
620745
})
621746
})
622-
623747
})
624748

625749
func expectCommittedAndPushed(conditions []metav1.Condition) {

docs/api/image-automation.md

Lines changed: 87 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,8 @@ into which will be interpolated the details of the change made.</p>
8888
<td>
8989
<code>gitRepositoryRef</code><br>
9090
<em>
91-
<a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.18/#localobjectreference-v1-core">
92-
Kubernetes core/v1.LocalObjectReference
91+
<a href="https://godoc.org/github.com/fluxcd/pkg/apis/meta#LocalObjectReference">
92+
github.com/fluxcd/pkg/apis/meta.LocalObjectReference
9393
</a>
9494
</em>
9595
</td>
@@ -106,7 +106,9 @@ string
106106
</em>
107107
</td>
108108
<td>
109-
<p>Branch gives the branch to clone from the git repository.</p>
109+
<p>Branch gives the branch to clone from the git repository. If
110+
<code>.spec.push</code> is not supplied, commits will also be pushed to
111+
this branch.</p>
110112
</td>
111113
</tr>
112114
</tbody>
@@ -206,7 +208,23 @@ CommitSpec
206208
</em>
207209
</td>
208210
<td>
209-
<p>Commit specifies how to commit to the git repo</p>
211+
<p>Commit specifies how to commit to the git repository.</p>
212+
</td>
213+
</tr>
214+
<tr>
215+
<td>
216+
<code>push</code><br>
217+
<em>
218+
<a href="#image.toolkit.fluxcd.io/v1alpha1.PushSpec">
219+
PushSpec
220+
</a>
221+
</em>
222+
</td>
223+
<td>
224+
<em>(Optional)</em>
225+
<p>Push specifies how and where to push commits made by the
226+
automation. If missing, commits are pushed (back) to
227+
<code>.spec.checkout.branch</code>.</p>
210228
</td>
211229
</tr>
212230
<tr>
@@ -311,7 +329,23 @@ CommitSpec
311329
</em>
312330
</td>
313331
<td>
314-
<p>Commit specifies how to commit to the git repo</p>
332+
<p>Commit specifies how to commit to the git repository.</p>
333+
</td>
334+
</tr>
335+
<tr>
336+
<td>
337+
<code>push</code><br>
338+
<em>
339+
<a href="#image.toolkit.fluxcd.io/v1alpha1.PushSpec">
340+
PushSpec
341+
</a>
342+
</em>
343+
</td>
344+
<td>
345+
<em>(Optional)</em>
346+
<p>Push specifies how and where to push commits made by the
347+
automation. If missing, commits are pushed (back) to
348+
<code>.spec.checkout.branch</code>.</p>
315349
</td>
316350
</tr>
317351
<tr>
@@ -434,6 +468,40 @@ github.com/fluxcd/pkg/apis/meta.ReconcileRequestStatus
434468
</table>
435469
</div>
436470
</div>
471+
<h3 id="image.toolkit.fluxcd.io/v1alpha1.PushSpec">PushSpec
472+
</h3>
473+
<p>
474+
(<em>Appears on:</em>
475+
<a href="#image.toolkit.fluxcd.io/v1alpha1.ImageUpdateAutomationSpec">ImageUpdateAutomationSpec</a>)
476+
</p>
477+
<p>PushSpec specifies how and where to push commits.</p>
478+
<div class="md-typeset__scrollwrap">
479+
<div class="md-typeset__table">
480+
<table>
481+
<thead>
482+
<tr>
483+
<th>Field</th>
484+
<th>Description</th>
485+
</tr>
486+
</thead>
487+
<tbody>
488+
<tr>
489+
<td>
490+
<code>branch</code><br>
491+
<em>
492+
string
493+
</em>
494+
</td>
495+
<td>
496+
<p>Branch specifies that commits should be pushed to the branch
497+
named. The branch is created using <code>.spec.checkout.branch</code> as the
498+
starting point, if it doesn&rsquo;t already exist.</p>
499+
</td>
500+
</tr>
501+
</tbody>
502+
</table>
503+
</div>
504+
</div>
437505
<h3 id="image.toolkit.fluxcd.io/v1alpha1.UpdateStrategy">UpdateStrategy
438506
</h3>
439507
<p>
@@ -466,6 +534,20 @@ UpdateStrategyName
466534
<p>Strategy names the strategy to be used.</p>
467535
</td>
468536
</tr>
537+
<tr>
538+
<td>
539+
<code>path</code><br>
540+
<em>
541+
string
542+
</em>
543+
</td>
544+
<td>
545+
<em>(Optional)</em>
546+
<p>Path to the directory containing the manifests to be updated.
547+
Defaults to &lsquo;None&rsquo;, which translates to the root path
548+
of the GitRepositoryRef.</p>
549+
</td>
550+
</tr>
469551
</tbody>
470552
</table>
471553
</div>

docs/spec/v1alpha1/imageupdateautomations.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,11 @@ type UpdateStrategy struct {
108108
// Strategy names the strategy to be used.
109109
// +required
110110
Strategy UpdateStrategyName `json:"strategy"`
111+
// Path to the directory containing the manifests to be updated.
112+
// Defaults to 'None', which translates to the root path
113+
// of the GitRepositoryRef.
114+
// +optional
115+
Path string `json:"path,omitempty"`
111116
}
112117
```
113118

0 commit comments

Comments
 (0)