Skip to content

Commit 29c8c1e

Browse files
authored
Merge pull request #37 from fluxcd/refactor-watcher
Refactor: Acquire artifacts with `fluxcd/pkg/http/fetch`
2 parents e6780ab + 78031f1 commit 29c8c1e

File tree

6 files changed

+45
-89
lines changed

6 files changed

+45
-89
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ manifests-release:
8989
CONTROLLER_GEN = $(shell pwd)/bin/controller-gen
9090
.PHONY: controller-gen
9191
controller-gen: ## Download controller-gen locally if necessary.
92-
$(call go-install-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.7.0)
92+
$(call go-install-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.8.0)
9393

9494
ENVTEST_ASSETS_DIR=$(shell pwd)/testbin
9595
ENVTEST_KUBERNETES_VERSION?=latest

config/rbac/role.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
---
32
apiVersion: rbac.authorization.k8s.io/v1
43
kind: ClusterRole
Lines changed: 20 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright 2020, 2021 The Flux authors
2+
Copyright 2022 The Flux authors
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
@@ -17,27 +17,36 @@ limitations under the License.
1717
package controllers
1818

1919
import (
20-
"bytes"
2120
"context"
22-
"crypto/sha256"
2321
"fmt"
24-
"io"
25-
"net/http"
2622
"os"
2723

28-
"k8s.io/apimachinery/pkg/runtime"
2924
ctrl "sigs.k8s.io/controller-runtime"
3025
"sigs.k8s.io/controller-runtime/pkg/builder"
3126
"sigs.k8s.io/controller-runtime/pkg/client"
3227

33-
"github.com/fluxcd/pkg/untar"
28+
"github.com/fluxcd/pkg/http/fetch"
29+
"github.com/fluxcd/pkg/tar"
3430
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
3531
)
3632

3733
// GitRepositoryWatcher watches GitRepository objects for revision changes
3834
type GitRepositoryWatcher struct {
3935
client.Client
40-
Scheme *runtime.Scheme
36+
artifactFetcher *fetch.ArchiveFetcher
37+
HttpRetry int
38+
}
39+
40+
func (r *GitRepositoryWatcher) SetupWithManager(mgr ctrl.Manager) error {
41+
r.artifactFetcher = fetch.NewArchiveFetcher(
42+
r.HttpRetry,
43+
tar.UnlimitedUntarSize,
44+
os.Getenv("SOURCE_CONTROLLER_LOCALHOST"),
45+
)
46+
47+
return ctrl.NewControllerManagedBy(mgr).
48+
For(&sourcev1.GitRepository{}, builder.WithPredicates(GitRepositoryRevisionChangePredicate{})).
49+
Complete(r)
4150
}
4251

4352
// +kubebuilder:rbac:groups=source.toolkit.fluxcd.io,resources=gitrepositories,verbs=get;list;watch
@@ -52,7 +61,8 @@ func (r *GitRepositoryWatcher) Reconcile(ctx context.Context, req ctrl.Request)
5261
return ctrl.Result{}, client.IgnoreNotFound(err)
5362
}
5463

55-
log.Info("New revision detected", "revision", repository.Status.Artifact.Revision)
64+
artifact := repository.Status.Artifact
65+
log.Info("New revision detected", "revision", artifact.Revision)
5666

5767
// create tmp dir
5868
tmpDir, err := os.MkdirTemp("", repository.Name)
@@ -62,12 +72,10 @@ func (r *GitRepositoryWatcher) Reconcile(ctx context.Context, req ctrl.Request)
6272
defer os.RemoveAll(tmpDir)
6373

6474
// download and extract artifact
65-
summary, err := r.fetchArtifact(ctx, repository, tmpDir)
66-
if err != nil {
75+
if err := r.artifactFetcher.Fetch(artifact.URL, artifact.Checksum, tmpDir); err != nil {
6776
log.Error(err, "unable to fetch artifact")
6877
return ctrl.Result{}, err
6978
}
70-
log.Info(summary)
7179

7280
// list artifact content
7381
files, err := os.ReadDir(tmpDir)
@@ -82,71 +90,3 @@ func (r *GitRepositoryWatcher) Reconcile(ctx context.Context, req ctrl.Request)
8290

8391
return ctrl.Result{}, nil
8492
}
85-
86-
func (r *GitRepositoryWatcher) SetupWithManager(mgr ctrl.Manager) error {
87-
return ctrl.NewControllerManagedBy(mgr).
88-
For(&sourcev1.GitRepository{}, builder.WithPredicates(GitRepositoryRevisionChangePredicate{})).
89-
Complete(r)
90-
}
91-
92-
func (r *GitRepositoryWatcher) fetchArtifact(ctx context.Context, repository sourcev1.GitRepository, dir string) (string, error) {
93-
if repository.Status.Artifact == nil {
94-
return "", fmt.Errorf("respository %s does not containt an artifact", repository.Name)
95-
}
96-
97-
url := repository.Status.Artifact.URL
98-
99-
// for local run:
100-
// kubectl -n flux-system port-forward svc/source-controller 8080:80
101-
// export SOURCE_HOST=localhost:8080
102-
if hostname := os.Getenv("SOURCE_HOST"); hostname != "" {
103-
url = fmt.Sprintf("http://%s/gitrepository/%s/%s/latest.tar.gz", hostname, repository.Namespace, repository.Name)
104-
}
105-
106-
// download the tarball
107-
req, err := http.NewRequest("GET", url, nil)
108-
if err != nil {
109-
return "", fmt.Errorf("failed to create HTTP request, error: %w", err)
110-
}
111-
112-
resp, err := http.DefaultClient.Do(req.WithContext(ctx))
113-
if err != nil {
114-
return "", fmt.Errorf("failed to download artifact from %s, error: %w", url, err)
115-
}
116-
defer resp.Body.Close()
117-
118-
// check response
119-
if resp.StatusCode != http.StatusOK {
120-
return "", fmt.Errorf("failed to download artifact, status: %s", resp.Status)
121-
}
122-
123-
var buf bytes.Buffer
124-
125-
// verify checksum matches origin
126-
if err := r.verifyArtifact(repository.GetArtifact(), &buf, resp.Body); err != nil {
127-
return "", err
128-
}
129-
130-
// extract
131-
summary, err := untar.Untar(&buf, dir)
132-
if err != nil {
133-
return "", fmt.Errorf("faild to untar artifact, error: %w", err)
134-
}
135-
136-
return summary, nil
137-
}
138-
139-
func (r *GitRepositoryWatcher) verifyArtifact(artifact *sourcev1.Artifact, buf *bytes.Buffer, reader io.Reader) error {
140-
hasher := sha256.New()
141-
mw := io.MultiWriter(hasher, buf)
142-
if _, err := io.Copy(mw, reader); err != nil {
143-
return err
144-
}
145-
146-
if checksum := fmt.Sprintf("%x", hasher.Sum(nil)); checksum != artifact.Checksum {
147-
return fmt.Errorf("failed to verify artifact: computed checksum '%s' doesn't match advertised '%s'",
148-
checksum, artifact.Checksum)
149-
}
150-
151-
return nil
152-
}

go.mod

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ module github.com/fluxcd/source-watcher
33
go 1.18
44

55
require (
6+
github.com/fluxcd/pkg/http/fetch v0.1.0
67
github.com/fluxcd/pkg/runtime v0.20.0
7-
github.com/fluxcd/pkg/untar v0.2.0
8+
github.com/fluxcd/pkg/tar v0.1.0
89
github.com/fluxcd/source-controller/api v0.30.0
910
github.com/spf13/pflag v1.0.5
1011
k8s.io/apimachinery v0.25.2
@@ -18,6 +19,7 @@ require (
1819
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
1920
github.com/beorn7/perks v1.0.1 // indirect
2021
github.com/cespare/xxhash/v2 v2.1.2 // indirect
22+
github.com/cyphar/filepath-securejoin v0.2.3 // indirect
2123
github.com/davecgh/go-spew v1.1.1 // indirect
2224
github.com/emicklei/go-restful/v3 v3.8.0 // indirect
2325
github.com/evanphx/json-patch/v5 v5.6.0 // indirect
@@ -36,6 +38,8 @@ require (
3638
github.com/google/go-cmp v0.5.9 // indirect
3739
github.com/google/gofuzz v1.2.0 // indirect
3840
github.com/google/uuid v1.3.0 // indirect
41+
github.com/hashicorp/go-cleanhttp v0.5.1 // indirect
42+
github.com/hashicorp/go-retryablehttp v0.7.1 // indirect
3943
github.com/imdario/mergo v0.3.12 // indirect
4044
github.com/josharian/intern v1.0.0 // indirect
4145
github.com/json-iterator/go v1.1.12 // indirect

go.sum

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnht
7777
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
7878
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
7979
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
80+
github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI=
81+
github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
8082
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
8183
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
8284
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -99,10 +101,13 @@ github.com/fluxcd/pkg/apis/acl v0.1.0 h1:EoAl377hDQYL3WqanWCdifauXqXbMyFuK82NnX6
99101
github.com/fluxcd/pkg/apis/acl v0.1.0/go.mod h1:zfEZzz169Oap034EsDhmCAGgnWlcWmIObZjYMusoXS8=
100102
github.com/fluxcd/pkg/apis/meta v0.17.0 h1:Y2dfo1syHZDb9Mexjr2SWdcj1FnxnRXm015hEnhl6wU=
101103
github.com/fluxcd/pkg/apis/meta v0.17.0/go.mod h1:GrOVzWXiu22XjLNgLLe2EBYhQPqZetes5SIADb4bmHE=
104+
github.com/fluxcd/pkg/http/fetch v0.1.0 h1:Ig/kZuM0+jHBJnwHn5UUseTKIYD5w8X4bInJyuyOZKI=
105+
github.com/fluxcd/pkg/http/fetch v0.1.0/go.mod h1:1CjOSfn7aOeHf2ZRA2+GTKHg442zN6X/fSys3a0KLC0=
102106
github.com/fluxcd/pkg/runtime v0.20.0 h1:F9q9wap0BhjQszboUroJrYOB1C831zkQwTAk2tlMIQc=
103107
github.com/fluxcd/pkg/runtime v0.20.0/go.mod h1:KVHNQMhccuLTjMDFVCr/SF+4Z554bcMH1LncC4sQf8o=
104-
github.com/fluxcd/pkg/untar v0.2.0 h1:sJXU+FbJcNUb2ffLJNjeR3hwt3X2loVpOMlCUjyFw6E=
105-
github.com/fluxcd/pkg/untar v0.2.0/go.mod h1:33AyoWaPpjX/xXpczcfhQh2AkB63TFwiR2YwROtv23E=
108+
github.com/fluxcd/pkg/tar v0.1.0 h1:ObyUml8NJtGQtz/cRgexd7HU2mQsTmgjz2dtX4xdnng=
109+
github.com/fluxcd/pkg/tar v0.1.0/go.mod h1:w0/TOC7kwBJhnSJn7TCABkc/I7ib1f2Yz6vOsbLBnhw=
110+
github.com/fluxcd/pkg/testserver v0.3.0 h1:oyZW6YWHVZR7FRVNu7lN9F5H808TD2jCzBm8CenFoi0=
106111
github.com/fluxcd/source-controller/api v0.30.0 h1:rPVPpwXcYG2n0DTRcRagfGDiccvCib5S09K5iMjlpRU=
107112
github.com/fluxcd/source-controller/api v0.30.0/go.mod h1:UkjAqQ6QAXNNesNQDTArTeiTp+UuhOUIA+JyFhGP/+Q=
108113
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
@@ -219,6 +224,12 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+
219224
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
220225
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
221226
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
227+
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
228+
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
229+
github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
230+
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
231+
github.com/hashicorp/go-retryablehttp v0.7.1 h1:sUiuQAnLlbvmExtFQs72iFW/HXeUn8Z1aJLQ4LJJbTQ=
232+
github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
222233
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
223234
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
224235
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
@@ -272,7 +283,7 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA
272283
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
273284
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
274285
github.com/onsi/ginkgo/v2 v2.1.6 h1:Fx2POJZfKRQcM1pH49qSZiYeu319wji004qX+GDovrU=
275-
github.com/onsi/gomega v1.20.2 h1:8uQq0zMgLEfa0vRrrBgaJF2gyW9Da9BmfGV+OyUzfkY=
286+
github.com/onsi/gomega v1.21.1 h1:OB/euWYIExnPBohllTicTHmGTrMaqJ67nIu80j0/uEM=
276287
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
277288
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
278289
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=

main.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright 2020, 2021 The Flux authors
2+
Copyright 2022 The Flux authors
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
@@ -48,13 +48,15 @@ func main() {
4848
var (
4949
metricsAddr string
5050
enableLeaderElection bool
51+
httpRetry int
5152
logOptions logger.Options
5253
)
5354

5455
flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.")
5556
flag.BoolVar(&enableLeaderElection, "enable-leader-election", false,
5657
"Enable leader election for controller manager. "+
5758
"Enabling this will ensure there is only one active controller manager.")
59+
flag.IntVar(&httpRetry, "http-retry", 9, "The maximum number of retries when failing to fetch artifacts over HTTP.")
5860
logOptions.BindFlags(flag.CommandLine)
5961
flag.Parse()
6062

@@ -74,8 +76,8 @@ func main() {
7476
}
7577

7678
if err = (&controllers.GitRepositoryWatcher{
77-
Client: mgr.GetClient(),
78-
Scheme: mgr.GetScheme(),
79+
Client: mgr.GetClient(),
80+
HttpRetry: httpRetry,
7981
}).SetupWithManager(mgr); err != nil {
8082
setupLog.Error(err, "unable to create controller", "controller", "GitRepositoryWatcher")
8183
os.Exit(1)

0 commit comments

Comments
 (0)