Merge pull request #411 from fluxcd/gitrepository-reconciler

Rewrite `GitRepositoryReconciler` to new standards
This commit is contained in:
Hidde Beydals 2021-08-03 14:25:49 +02:00 committed by GitHub
commit becd5f824c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 2432 additions and 1544 deletions

View File

@ -3,7 +3,11 @@ module github.com/fluxcd/source-controller/api
go 1.16
require (
github.com/fluxcd/pkg/apis/meta v0.10.0
k8s.io/apimachinery v0.21.1
sigs.k8s.io/controller-runtime v0.9.0
github.com/fluxcd/pkg/apis/meta v0.11.0-rc.1
// TODO(hidde): introduction of the runtime package is temporary, and the dependency should be removed as soon as
// all APIs have been updated to the runtime standards (more specifically; have dropped their condition modifying
// functions).
github.com/fluxcd/pkg/runtime v0.13.0-rc.2
k8s.io/apimachinery v0.21.3
sigs.k8s.io/controller-runtime v0.9.3
)

View File

@ -88,10 +88,13 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ=
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch v4.11.0+incompatible h1:glyUF9yIYtMHzn8xaKw5rMhdWcwsYV8dZHIq5567/xs=
github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fluxcd/pkg/apis/meta v0.10.0 h1:N7wVGHC1cyPdT87hrDC7UwCwRwnZdQM46PBSLjG2rlE=
github.com/fluxcd/pkg/apis/meta v0.10.0/go.mod h1:CW9X9ijMTpNe7BwnokiUOrLl/h13miwVr/3abEQLbKE=
github.com/fluxcd/pkg/apis/meta v0.11.0-rc.1 h1:RHHrztAFv9wmjM+Pk7Svt1UdD+1SdnQSp76MWFiM7Hg=
github.com/fluxcd/pkg/apis/meta v0.11.0-rc.1/go.mod h1:yUblM2vg+X8TE3A2VvJfdhkGmg+uqBlSPkLk7dxi0UM=
github.com/fluxcd/pkg/runtime v0.13.0-rc.2 h1:+4uTEg+CU++hlr7NpOP4KYp60MtHDOgYvpz/74tbATg=
github.com/fluxcd/pkg/runtime v0.13.0-rc.2/go.mod h1:TmvE2cJl1QkgZNmmlr7XUKoWDQwUiM5/wTUxXsQVoc8=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
@ -110,6 +113,7 @@ github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc=
github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
github.com/go-logr/zapr v0.4.0 h1:uc1uML3hRYL9/ZZPdgHS/n8Nzo+eaYL/Efxkkamf7OM=
github.com/go-logr/zapr v0.4.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk=
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
@ -150,6 +154,7 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
@ -158,7 +163,6 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
@ -178,6 +182,7 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=
github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw=
github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
@ -192,9 +197,11 @@ github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBt
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-retryablehttp v0.6.8/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
@ -212,6 +219,7 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
@ -297,6 +305,7 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@ -367,6 +376,7 @@ github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijb
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
@ -377,11 +387,14 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@ -424,6 +437,7 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -454,12 +468,15 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -469,6 +486,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -510,12 +528,16 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 h1:JWgyZ1qgdTaF3N3oxC+MdTV7qvEEgHo3otj+HB5CM7Q=
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -530,6 +552,8 @@ golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6 h1:Vv0JUPWTyeqUq42B2WJ1FeIDjjvGKoA2Ss+Ts0lAVbs=
golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -571,6 +595,7 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -592,6 +617,7 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
@ -632,6 +658,7 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@ -671,33 +698,40 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
k8s.io/api v0.21.1 h1:94bbZ5NTjdINJEdzOkpS4vdPhkb1VFpTYC9zh43f75c=
k8s.io/api v0.21.1/go.mod h1:FstGROTmsSHBarKc8bylzXih8BLNYTiS3TZcsoEDg2s=
k8s.io/apiextensions-apiserver v0.21.1/go.mod h1:KESQFCGjqVcVsZ9g0xX5bacMjyX5emuWcS2arzdEouA=
k8s.io/apimachinery v0.21.1 h1:Q6XuHGlj2xc+hlMCvqyYfbv3H7SRGn2c8NycxJquDVs=
k8s.io/apimachinery v0.21.1/go.mod h1:jbreFvJo3ov9rj7eWT7+sYiRx+qZuCYXwWT1bcDswPY=
k8s.io/apiserver v0.21.1/go.mod h1:nLLYZvMWn35glJ4/FZRhzLG/3MPxAaZTgV4FJZdr+tY=
k8s.io/client-go v0.21.1/go.mod h1:/kEw4RgW+3xnBGzvp9IWxKSNA+lXn3A7AuH3gdOAzLs=
k8s.io/code-generator v0.21.1/go.mod h1:hUlps5+9QaTrKx+jiM4rmq7YmH8wPOIko64uZCHDh6Q=
k8s.io/component-base v0.21.1/go.mod h1:NgzFZ2qu4m1juby4TnrmpR8adRk6ka62YdH5DkIIyKA=
k8s.io/api v0.21.2 h1:vz7DqmRsXTCSa6pNxXwQ1IYeAZgdIsua+DZU+o+SX3Y=
k8s.io/api v0.21.2/go.mod h1:Lv6UGJZ1rlMI1qusN8ruAp9PUBFyBwpEHAdG24vIsiU=
k8s.io/apiextensions-apiserver v0.21.2 h1:+exKMRep4pDrphEafRvpEi79wTnCFMqKf8LBtlA3yrE=
k8s.io/apiextensions-apiserver v0.21.2/go.mod h1:+Axoz5/l3AYpGLlhJDfcVQzCerVYq3K3CvDMvw6X1RA=
k8s.io/apimachinery v0.21.2/go.mod h1:CdTY8fU/BlvAbJ2z/8kBwimGki5Zp8/fbVuLY8gJumM=
k8s.io/apimachinery v0.21.3 h1:3Ju4nvjCngxxMYby0BimUk+pQHPOQp3eCGChk5kfVII=
k8s.io/apimachinery v0.21.3/go.mod h1:H/IM+5vH9kZRNJ4l3x/fXP/5bOPJaVP/guptnZPeCFI=
k8s.io/apiserver v0.21.2/go.mod h1:lN4yBoGyiNT7SC1dmNk0ue6a5Wi6O3SWOIw91TsucQw=
k8s.io/client-go v0.21.2 h1:Q1j4L/iMN4pTw6Y4DWppBoUxgKO8LbffEMVEV00MUp0=
k8s.io/client-go v0.21.2/go.mod h1:HdJ9iknWpbl3vMGtib6T2PyI/VYxiZfq936WNVHBRrA=
k8s.io/code-generator v0.21.2/go.mod h1:8mXJDCB7HcRo1xiEQstcguZkbxZaqeUOrO9SsicWs3U=
k8s.io/component-base v0.21.2/go.mod h1:9lvmIThzdlrJj5Hp8Z/TOgIkdfsNARQ1pT+3PByuiuc=
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
k8s.io/klog/v2 v2.8.0 h1:Q3gmuM9hKEjefWFFYF0Mat+YyFJvsUyYuwyNNJ5C9Ts=
k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7 h1:vEx13qjvaZ4yfObSSXW7BrMc/KQBBT/Jyee8XtLf4x0=
k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE=
k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
k8s.io/utils v0.0.0-20210527160623-6fdb442a123b h1:MSqsVQ3pZvPGTqCjptfimO2WjG7A9un2zcpiHkA6M/s=
k8s.io/utils v0.0.0-20210527160623-6fdb442a123b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
sigs.k8s.io/controller-runtime v0.9.0 h1:ZIZ/dtpboPSbZYY7uUz2OzrkaBTOThx2yekLtpGB+zY=
sigs.k8s.io/controller-runtime v0.9.0/go.mod h1:TgkfvrhhEw3PlI0BRL/5xM+89y3/yc0ZDfdbTl84si8=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.19/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
sigs.k8s.io/controller-runtime v0.9.2/go.mod h1:TxzMCHyEUpaeuOiZx/bIdc2T81vfs/aKdvJt9wuu0zk=
sigs.k8s.io/controller-runtime v0.9.3 h1:n075bHQ1wb8hpX7C27pNrqsb0fj8mcfCQfNX+oKTbYE=
sigs.k8s.io/controller-runtime v0.9.3/go.mod h1:TxzMCHyEUpaeuOiZx/bIdc2T81vfs/aKdvJt9wuu0zk=
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/structured-merge-diff/v4 v4.1.0 h1:C4r9BgJ98vrKnnVCjwCSXcWjWe0NKcUQkmzDXZXGwH8=
sigs.k8s.io/structured-merge-diff/v4 v4.1.0/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno=
sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=

View File

@ -17,7 +17,10 @@ limitations under the License.
package v1beta1
import (
"time"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/pkg/runtime/conditions"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
@ -119,7 +122,7 @@ func BucketProgressing(bucket Bucket) Bucket {
bucket.Status.ObservedGeneration = bucket.Generation
bucket.Status.URL = ""
bucket.Status.Conditions = []metav1.Condition{}
meta.SetResourceCondition(&bucket, meta.ReadyCondition, metav1.ConditionUnknown, meta.ProgressingReason, "reconciliation in progress")
conditions.MarkUnknown(&bucket, meta.ReadyCondition, meta.ProgressingReason, "reconciliation in progress")
return bucket
}
@ -129,14 +132,14 @@ func BucketProgressing(bucket Bucket) Bucket {
func BucketReady(bucket Bucket, artifact Artifact, url, reason, message string) Bucket {
bucket.Status.Artifact = &artifact
bucket.Status.URL = url
meta.SetResourceCondition(&bucket, meta.ReadyCondition, metav1.ConditionTrue, reason, message)
conditions.MarkTrue(&bucket, meta.ReadyCondition, reason, message)
return bucket
}
// BucketNotReady sets the meta.ReadyCondition on the Bucket to 'False', with
// the given reason and message. It returns the modified Bucket.
func BucketNotReady(bucket Bucket, reason, message string) Bucket {
meta.SetResourceCondition(&bucket, meta.ReadyCondition, metav1.ConditionFalse, reason, message)
conditions.MarkFalse(&bucket, meta.ReadyCondition, reason, message)
return bucket
}
@ -151,22 +154,38 @@ func BucketReadyMessage(bucket Bucket) string {
return ""
}
// GetArtifact returns the latest artifact from the source if present in the
// status sub-resource.
// GetConditions returns the status conditions of the object.
func (in Bucket) GetConditions() []metav1.Condition {
return in.Status.Conditions
}
// SetConditions sets the status conditions on the object.
func (in *Bucket) SetConditions(conditions []metav1.Condition) {
in.Status.Conditions = conditions
}
// GetRequeueAfter returns the duration after which the source must be reconciled again.
func (in Bucket) GetRequeueAfter() time.Duration {
return in.Spec.Interval.Duration
}
// GetInterval returns the interval at which the source is reconciled.
// Deprecated: use GetRequeueAfter instead.
func (in Bucket) GetInterval() metav1.Duration {
return in.Spec.Interval
}
// GetArtifact returns the latest artifact from the source if present in the status sub-resource.
func (in *Bucket) GetArtifact() *Artifact {
return in.Status.Artifact
}
// GetStatusConditions returns a pointer to the Status.Conditions slice
// GetStatusConditions returns a pointer to the Status.Conditions slice.
// Deprecated: use GetConditions instead.
func (in *Bucket) GetStatusConditions() *[]metav1.Condition {
return &in.Status.Conditions
}
// GetInterval returns the interval at which the source is updated.
func (in *Bucket) GetInterval() metav1.Duration {
return in.Spec.Interval
}
// +genclient
// +genclient:Namespaced
// +kubebuilder:object:root=true

View File

@ -17,8 +17,9 @@ limitations under the License.
package v1beta1
import (
"time"
"github.com/fluxcd/pkg/apis/meta"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
@ -32,6 +33,30 @@ const (
LibGit2Implementation = "libgit2"
)
const (
// ArtifactUnavailableCondition indicates there is no Artifact available for the Source.
// This is a "negative polarity" or "abnormal-true" type, and is only present on the resource if it is True.
ArtifactUnavailableCondition string = "ArtifactUnavailable"
// CheckoutFailedCondition indicates a transient or persistent checkout failure. If True, observations on the
// upstream Source revision are not possible, and the Artifact available for the Source may be outdated.
// This is a "negative polarity" or "abnormal-true" type, and is only present on the resource if it is True.
CheckoutFailedCondition string = "CheckoutFailed"
// SourceVerifiedCondition indicates the integrity of the Source has been verified. If True, the integrity check
// succeeded. If False, it failed. The Condition is only present on the resource if the integrity has been verified.
SourceVerifiedCondition string = "SourceVerified"
// IncludeUnavailableCondition indicates one of the includes is not available. For example, because it does not
// exist, or does not have an Artifact.
// This is a "negative polarity" or "abnormal-true" type, and is only present on the resource if it is True.
IncludeUnavailableCondition string = "IncludeUnavailable"
// ArtifactOutdatedCondition indicates the current Artifact of the Source is outdated.
// This is a "negative polarity" or "abnormal-true" type, and is only present on the resource if it is True.
ArtifactOutdatedCondition string = "ArtifactOutdated"
)
// GitRepositorySpec defines the desired state of a Git repository.
type GitRepositorySpec struct {
// The repository URL, can be a HTTP/S or SSH address.
@ -40,10 +65,8 @@ type GitRepositorySpec struct {
URL string `json:"url"`
// The secret name containing the Git credentials.
// For HTTPS repositories the secret must contain username and password
// fields.
// For SSH repositories the secret must contain identity, identity.pub and
// known_hosts fields.
// For HTTPS repositories the secret must contain username and password fields.
// For SSH repositories the secret must contain 'identity', 'identity.pub' and 'known_hosts' fields.
// +optional
SecretRef *meta.LocalObjectReference `json:"secretRef,omitempty"`
@ -61,16 +84,16 @@ type GitRepositorySpec struct {
// +optional
Reference *GitRepositoryRef `json:"ref,omitempty"`
// Verify OpenPGP signature for the Git commit HEAD points to.
// Verification defines the configuration to verify the OpenPGP signature for the Git commit HEAD points to.
// +optional
Verification *GitRepositoryVerification `json:"verify,omitempty"`
// Ignore overrides the set of excluded patterns in the .sourceignore format
// (which is the same as .gitignore). If not provided, a default will be used,
// consult the documentation for your version to find out what those are.
// Ignore overrides the set of excluded patterns in the .sourceignore format (which is the same as .gitignore).
// If not provided, a default will be used, consult the documentation for your version to find out what those are.
// +optional
Ignore *string `json:"ignore,omitempty"`
// Suspend tells the controller to suspend the reconciliation of this source.
// This flag tells the controller to suspend the reconciliation of this source.
// +optional
Suspend bool `json:"suspend,omitempty"`
@ -82,13 +105,13 @@ type GitRepositorySpec struct {
// +optional
GitImplementation string `json:"gitImplementation,omitempty"`
// When enabled, after the clone is created, initializes all submodules within,
// using their default settings.
// When enabled, after the clone is created, initializes all submodules within, using their default settings.
// This option is available only when using the 'go-git' GitImplementation.
// +optional
RecurseSubmodules bool `json:"recurseSubmodules,omitempty"`
// Extra git repositories to map into the repository
// Include defines a list of GitRepository resources which artifacts should be included in the artifact produced for
// this resource.
Include []GitRepositoryInclude `json:"include,omitempty"`
}
@ -139,11 +162,11 @@ type GitRepositoryRef struct {
// GitRepositoryVerification defines the OpenPGP signature verification process.
type GitRepositoryVerification struct {
// Mode describes what git object should be verified, currently ('head').
// Mode describes what Git object should be verified, currently ('head').
// +kubebuilder:validation:Enum=head
Mode string `json:"mode"`
// The secret name containing the public keys of all trusted Git authors.
// SecretRef containing the public keys of all trusted Git authors.
SecretRef meta.LocalObjectReference `json:"secretRef,omitempty"`
}
@ -157,8 +180,7 @@ type GitRepositoryStatus struct {
// +optional
Conditions []metav1.Condition `json:"conditions,omitempty"`
// URL is the download link for the artifact output of the last repository
// sync.
// URL is the download link for the artifact output of the last repository sync.
// +optional
URL string `json:"url,omitempty"`
@ -174,73 +196,45 @@ type GitRepositoryStatus struct {
}
const (
// GitOperationSucceedReason represents the fact that the git clone, pull
// and checkout operations succeeded.
// GitOperationSucceedReason represents the fact that the git clone, pull and checkout operations succeeded.
GitOperationSucceedReason string = "GitOperationSucceed"
// GitOperationFailedReason represents the fact that the git clone, pull or
// checkout operations failed.
// GitOperationFailedReason represents the fact that the git clone, pull or checkout operations failed.
GitOperationFailedReason string = "GitOperationFailed"
)
// GitRepositoryProgressing resets the conditions of the GitRepository to
// metav1.Condition of type meta.ReadyCondition with status 'Unknown' and
// meta.ProgressingReason reason and message. It returns the modified
// GitRepository.
func GitRepositoryProgressing(repository GitRepository) GitRepository {
repository.Status.ObservedGeneration = repository.Generation
repository.Status.URL = ""
repository.Status.Conditions = []metav1.Condition{}
meta.SetResourceCondition(&repository, meta.ReadyCondition, metav1.ConditionUnknown, meta.ProgressingReason, "reconciliation in progress")
return repository
// GetConditions returns the status conditions of the object.
func (in GitRepository) GetConditions() []metav1.Condition {
return in.Status.Conditions
}
// GitRepositoryReady sets the given Artifact and URL on the GitRepository and
// sets the meta.ReadyCondition to 'True', with the given reason and message. It
// returns the modified GitRepository.
func GitRepositoryReady(repository GitRepository, artifact Artifact, includedArtifacts []*Artifact, url, reason, message string) GitRepository {
repository.Status.Artifact = &artifact
repository.Status.IncludedArtifacts = includedArtifacts
repository.Status.URL = url
meta.SetResourceCondition(&repository, meta.ReadyCondition, metav1.ConditionTrue, reason, message)
return repository
// SetConditions sets the status conditions on the object.
func (in *GitRepository) SetConditions(conditions []metav1.Condition) {
in.Status.Conditions = conditions
}
// GitRepositoryNotReady sets the meta.ReadyCondition on the given GitRepository
// to 'False', with the given reason and message. It returns the modified
// GitRepository.
func GitRepositoryNotReady(repository GitRepository, reason, message string) GitRepository {
meta.SetResourceCondition(&repository, meta.ReadyCondition, metav1.ConditionFalse, reason, message)
return repository
// GetRequeueAfter returns the duration after which the source must be reconciled again.
func (in GitRepository) GetRequeueAfter() time.Duration {
return in.Spec.Interval.Duration
}
// GitRepositoryReadyMessage returns the message of the metav1.Condition of type
// meta.ReadyCondition with status 'True' if present, or an empty string.
func GitRepositoryReadyMessage(repository GitRepository) string {
if c := apimeta.FindStatusCondition(repository.Status.Conditions, meta.ReadyCondition); c != nil {
if c.Status == metav1.ConditionTrue {
return c.Message
}
}
return ""
// GetInterval returns the interval at which the source is reconciled.
// Deprecated: use GetRequeueAfter instead.
func (in GitRepository) GetInterval() metav1.Duration {
return in.Spec.Interval
}
// GetArtifact returns the latest artifact from the source if present in the
// status sub-resource.
// GetArtifact returns the latest artifact from the source if present in the status sub-resource.
func (in *GitRepository) GetArtifact() *Artifact {
return in.Status.Artifact
}
// GetStatusConditions returns a pointer to the Status.Conditions slice
// GetStatusConditions returns a pointer to the Status.Conditions slice.
// Deprecated: use GetConditions instead.
func (in *GitRepository) GetStatusConditions() *[]metav1.Condition {
return &in.Status.Conditions
}
// GetInterval returns the interval at which the source is updated.
func (in *GitRepository) GetInterval() metav1.Duration {
return in.Spec.Interval
}
// +genclient
// +genclient:Namespaced
// +kubebuilder:object:root=true

View File

@ -17,7 +17,10 @@ limitations under the License.
package v1beta1
import (
"time"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/pkg/runtime/conditions"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
@ -129,7 +132,7 @@ func HelmChartProgressing(chart HelmChart) HelmChart {
chart.Status.ObservedGeneration = chart.Generation
chart.Status.URL = ""
chart.Status.Conditions = []metav1.Condition{}
meta.SetResourceCondition(&chart, meta.ReadyCondition, metav1.ConditionUnknown, meta.ProgressingReason, "reconciliation in progress")
conditions.MarkUnknown(&chart, meta.ReadyCondition, meta.ProgressingReason, "reconciliation in progress")
return chart
}
@ -139,7 +142,7 @@ func HelmChartProgressing(chart HelmChart) HelmChart {
func HelmChartReady(chart HelmChart, artifact Artifact, url, reason, message string) HelmChart {
chart.Status.Artifact = &artifact
chart.Status.URL = url
meta.SetResourceCondition(&chart, meta.ReadyCondition, metav1.ConditionTrue, reason, message)
conditions.MarkTrue(&chart, meta.ReadyCondition, reason, message)
return chart
}
@ -147,7 +150,7 @@ func HelmChartReady(chart HelmChart, artifact Artifact, url, reason, message str
// 'False', with the given reason and message. It returns the modified
// HelmChart.
func HelmChartNotReady(chart HelmChart, reason, message string) HelmChart {
meta.SetResourceCondition(&chart, meta.ReadyCondition, metav1.ConditionFalse, reason, message)
conditions.MarkFalse(&chart, meta.ReadyCondition, reason, message)
return chart
}
@ -162,22 +165,32 @@ func HelmChartReadyMessage(chart HelmChart) string {
return ""
}
// GetArtifact returns the latest artifact from the source if present in the
// status sub-resource.
// GetConditions returns the status conditions of the object.
func (in HelmChart) GetConditions() []metav1.Condition {
return in.Status.Conditions
}
// SetConditions sets the status conditions on the object.
func (in *HelmChart) SetConditions(conditions []metav1.Condition) {
in.Status.Conditions = conditions
}
// GetRequeueAfter returns the duration after which the source must be reconciled again.
func (in HelmChart) GetRequeueAfter() time.Duration {
return in.Spec.Interval.Duration
}
// GetInterval returns the interval at which the source is reconciled.
// Deprecated: use GetRequeueAfter instead.
func (in HelmChart) GetInterval() metav1.Duration {
return in.Spec.Interval
}
// GetArtifact returns the latest artifact from the source if present in the status sub-resource.
func (in *HelmChart) GetArtifact() *Artifact {
return in.Status.Artifact
}
// GetStatusConditions returns a pointer to the Status.Conditions slice
func (in *HelmChart) GetStatusConditions() *[]metav1.Condition {
return &in.Status.Conditions
}
// GetInterval returns the interval at which the source is updated.
func (in *HelmChart) GetInterval() metav1.Duration {
return in.Spec.Interval
}
// GetValuesFiles returns a merged list of ValuesFiles.
func (in *HelmChart) GetValuesFiles() []string {
valuesFiles := in.Spec.ValuesFiles
@ -189,6 +202,12 @@ func (in *HelmChart) GetValuesFiles() []string {
return valuesFiles
}
// GetStatusConditions returns a pointer to the Status.Conditions slice.
// Deprecated: use GetConditions instead.
func (in *HelmChart) GetStatusConditions() *[]metav1.Condition {
return &in.Status.Conditions
}
// +genclient
// +genclient:Namespaced
// +kubebuilder:object:root=true

View File

@ -17,7 +17,10 @@ limitations under the License.
package v1beta1
import (
"time"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/pkg/runtime/conditions"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
@ -107,7 +110,7 @@ func HelmRepositoryProgressing(repository HelmRepository) HelmRepository {
repository.Status.ObservedGeneration = repository.Generation
repository.Status.URL = ""
repository.Status.Conditions = []metav1.Condition{}
meta.SetResourceCondition(&repository, meta.ReadyCondition, metav1.ConditionUnknown, meta.ProgressingReason, "reconciliation in progress")
conditions.MarkUnknown(&repository, meta.ReadyCondition, meta.ProgressingReason, "reconciliation in progress")
return repository
}
@ -117,7 +120,7 @@ func HelmRepositoryProgressing(repository HelmRepository) HelmRepository {
func HelmRepositoryReady(repository HelmRepository, artifact Artifact, url, reason, message string) HelmRepository {
repository.Status.Artifact = &artifact
repository.Status.URL = url
meta.SetResourceCondition(&repository, meta.ReadyCondition, metav1.ConditionTrue, reason, message)
conditions.MarkTrue(&repository, meta.ReadyCondition, reason, message)
return repository
}
@ -125,7 +128,7 @@ func HelmRepositoryReady(repository HelmRepository, artifact Artifact, url, reas
// HelmRepository to 'False', with the given reason and message. It returns the
// modified HelmRepository.
func HelmRepositoryNotReady(repository HelmRepository, reason, message string) HelmRepository {
meta.SetResourceCondition(&repository, meta.ReadyCondition, metav1.ConditionFalse, reason, message)
conditions.MarkFalse(&repository, meta.ReadyCondition, reason, message)
return repository
}
@ -140,22 +143,38 @@ func HelmRepositoryReadyMessage(repository HelmRepository) string {
return ""
}
// GetArtifact returns the latest artifact from the source if present in the
// status sub-resource.
// GetConditions returns the status conditions of the object.
func (in HelmRepository) GetConditions() []metav1.Condition {
return in.Status.Conditions
}
// SetConditions sets the status conditions on the object.
func (in *HelmRepository) SetConditions(conditions []metav1.Condition) {
in.Status.Conditions = conditions
}
// GetRequeueAfter returns the duration after which the source must be reconciled again.
func (in HelmRepository) GetRequeueAfter() time.Duration {
return in.Spec.Interval.Duration
}
// GetInterval returns the interval at which the source is reconciled.
// Deprecated: use GetRequeueAfter instead.
func (in HelmRepository) GetInterval() metav1.Duration {
return in.Spec.Interval
}
// GetArtifact returns the latest artifact from the source if present in the status sub-resource.
func (in *HelmRepository) GetArtifact() *Artifact {
return in.Status.Artifact
}
// GetStatusConditions returns a pointer to the Status.Conditions slice
// GetStatusConditions returns a pointer to the Status.Conditions slice.
// Deprecated: use GetConditions instead.
func (in *HelmRepository) GetStatusConditions() *[]metav1.Condition {
return &in.Status.Conditions
}
// GetInterval returns the interval at which the source is updated.
func (in *HelmRepository) GetInterval() metav1.Duration {
return in.Spec.Interval
}
// +genclient
// +genclient:Namespaced
// +kubebuilder:object:root=true

View File

@ -17,6 +17,8 @@ limitations under the License.
package v1beta1
import (
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
@ -29,9 +31,12 @@ const (
// Source interface must be supported by all API types.
// +k8s:deepcopy-gen=false
type Source interface {
// GetRequeueAfter returns the duration after which the source must be reconciled again.
GetRequeueAfter() time.Duration
// GetArtifact returns the latest artifact from the source if present in the
// status sub-resource.
GetArtifact() *Artifact
// GetInterval returns the interval at which the source is updated.
// Deprecated: use GetRequeueAfter instead.
GetInterval() metav1.Duration
}

View File

@ -74,7 +74,7 @@ spec:
description: The name of the secret containing authentication credentials for the Bucket.
properties:
name:
description: Name of the referent
description: Name of the referent.
type: string
required:
- name
@ -162,7 +162,7 @@ spec:
type: object
type: array
lastHandledReconcileAt:
description: LastHandledReconcileAt holds the value of the most recent reconcile request value, so a change can be detected.
description: LastHandledReconcileAt holds the value of the most recent reconcile request value, so a change of the annotation value can be detected.
type: string
observedGeneration:
description: ObservedGeneration is the last observed generation.

View File

@ -58,7 +58,7 @@ spec:
description: Ignore overrides the set of excluded patterns in the .sourceignore format (which is the same as .gitignore). If not provided, a default will be used, consult the documentation for your version to find out what those are.
type: string
include:
description: Extra git repositories to map into the repository
description: Include defines a list of GitRepository resources which artifacts should be included in the artifact produced for this resource.
items:
description: GitRepositoryInclude defines a source with a from and to path.
properties:
@ -69,7 +69,7 @@ spec:
description: Reference to a GitRepository to include.
properties:
name:
description: Name of the referent
description: Name of the referent.
type: string
required:
- name
@ -105,16 +105,16 @@ spec:
type: string
type: object
secretRef:
description: The secret name containing the Git credentials. For HTTPS repositories the secret must contain username and password fields. For SSH repositories the secret must contain identity, identity.pub and known_hosts fields.
description: The secret name containing the Git credentials. For HTTPS repositories the secret must contain username and password fields. For SSH repositories the secret must contain 'identity', 'identity.pub' and 'known_hosts' fields.
properties:
name:
description: Name of the referent
description: Name of the referent.
type: string
required:
- name
type: object
suspend:
description: This flag tells the controller to suspend the reconciliation of this source.
description: Suspend tells the controller to suspend the reconciliation of this source. This flag tells the controller to suspend the reconciliation of this source.
type: boolean
timeout:
default: 20s
@ -125,18 +125,18 @@ spec:
pattern: ^(http|https|ssh)://
type: string
verify:
description: Verify OpenPGP signature for the Git commit HEAD points to.
description: Verification defines the configuration to verify the OpenPGP signature for the Git commit HEAD points to.
properties:
mode:
description: Mode describes what git object should be verified, currently ('head').
description: Mode describes what Git object should be verified, currently ('head').
enum:
- head
type: string
secretRef:
description: The secret name containing the public keys of all trusted Git authors.
description: SecretRef containing the public keys of all trusted Git authors.
properties:
name:
description: Name of the referent
description: Name of the referent.
type: string
required:
- name
@ -245,7 +245,7 @@ spec:
type: object
type: array
lastHandledReconcileAt:
description: LastHandledReconcileAt holds the value of the most recent reconcile request value, so a change can be detected.
description: LastHandledReconcileAt holds the value of the most recent reconcile request value, so a change of the annotation value can be detected.
type: string
observedGeneration:
description: ObservedGeneration is the last observed generation.

View File

@ -173,7 +173,7 @@ spec:
type: object
type: array
lastHandledReconcileAt:
description: LastHandledReconcileAt holds the value of the most recent reconcile request value, so a change can be detected.
description: LastHandledReconcileAt holds the value of the most recent reconcile request value, so a change of the annotation value can be detected.
type: string
observedGeneration:
description: ObservedGeneration is the last observed generation.

View File

@ -57,7 +57,7 @@ spec:
description: The name of the secret containing authentication credentials for the Helm repository. For HTTP/S basic auth the secret must contain username and password fields. For TLS the secret must contain a certFile and keyFile, and/or caCert fields.
properties:
name:
description: Name of the referent
description: Name of the referent.
type: string
required:
- name
@ -147,7 +147,7 @@ spec:
type: object
type: array
lastHandledReconcileAt:
description: LastHandledReconcileAt holds the value of the most recent reconcile request value, so a change can be detected.
description: LastHandledReconcileAt holds the value of the most recent reconcile request value, so a change of the annotation value can be detected.
type: string
observedGeneration:
description: ObservedGeneration is the last observed generation.

View File

@ -1,23 +1,39 @@
/*
Copyright 2021 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package controllers
import sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
// hasArtifactUpdated returns true if any of the revisions in the current artifacts
// does not match any of the artifacts in the updated artifacts
func hasArtifactUpdated(current []*sourcev1.Artifact, updated []*sourcev1.Artifact) bool {
if len(current) != len(updated) {
type artifactSet []*sourcev1.Artifact
// Diff returns true if any of the revisions in the artifactSet does not match any of the given artifacts.
func (s artifactSet) Diff(set artifactSet) bool {
if len(s) != len(set) {
return true
}
OUTER:
for _, c := range current {
for _, u := range updated {
if u.HasRevision(c.Revision) {
continue OUTER
outer:
for _, j := range s {
for _, k := range set {
if k.HasRevision(j.Revision) {
continue outer
}
}
return true
}
return false
}

View File

@ -1,26 +1,40 @@
/*
Copyright 2021 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package controllers
import (
"testing"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
)
func TestHasUpdated(t *testing.T) {
func Test_artifactSet_Diff(t *testing.T) {
tests := []struct {
name string
current []*sourcev1.Artifact
updated []*sourcev1.Artifact
current artifactSet
updated artifactSet
expected bool
}{
{
name: "not updated single",
current: []*sourcev1.Artifact{
name: "one artifact, no diff",
current: artifactSet{
{
Revision: "foo",
},
},
updated: []*sourcev1.Artifact{
updated: artifactSet{
{
Revision: "foo",
},
@ -28,13 +42,13 @@ func TestHasUpdated(t *testing.T) {
expected: false,
},
{
name: "updated single",
current: []*sourcev1.Artifact{
name: "one artifact, diff",
current: artifactSet{
{
Revision: "foo",
},
},
updated: []*sourcev1.Artifact{
updated: artifactSet{
{
Revision: "bar",
},
@ -42,8 +56,8 @@ func TestHasUpdated(t *testing.T) {
expected: true,
},
{
name: "not updated multiple",
current: []*sourcev1.Artifact{
name: "multiple artifacts, no diff",
current: artifactSet{
{
Revision: "foo",
},
@ -51,7 +65,7 @@ func TestHasUpdated(t *testing.T) {
Revision: "bar",
},
},
updated: []*sourcev1.Artifact{
updated: artifactSet{
{
Revision: "foo",
},
@ -62,8 +76,8 @@ func TestHasUpdated(t *testing.T) {
expected: false,
},
{
name: "updated multiple",
current: []*sourcev1.Artifact{
name: "multiple artifacts, diff",
current: artifactSet{
{
Revision: "foo",
},
@ -71,7 +85,7 @@ func TestHasUpdated(t *testing.T) {
Revision: "bar",
},
},
updated: []*sourcev1.Artifact{
updated: artifactSet{
{
Revision: "foo",
},
@ -82,8 +96,8 @@ func TestHasUpdated(t *testing.T) {
expected: true,
},
{
name: "updated different artifact count",
current: []*sourcev1.Artifact{
name: "different artifact count",
current: artifactSet{
{
Revision: "foo",
},
@ -91,7 +105,7 @@ func TestHasUpdated(t *testing.T) {
Revision: "bar",
},
},
updated: []*sourcev1.Artifact{
updated: artifactSet{
{
Revision: "foo",
},
@ -101,7 +115,7 @@ func TestHasUpdated(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := hasArtifactUpdated(tt.current, tt.updated)
result := tt.current.Diff(tt.updated)
if result != tt.expected {
t.Errorf("Archive() result = %v, wantResult %v", result, tt.expected)
}

View File

@ -169,10 +169,10 @@ func (r *BucketReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr
log.Info(fmt.Sprintf("Reconciliation finished in %s, next run in %s",
time.Now().Sub(start).String(),
bucket.GetInterval().Duration.String(),
bucket.GetRequeueAfter().String(),
))
return ctrl.Result{RequeueAfter: bucket.GetInterval().Duration}, nil
return ctrl.Result{RequeueAfter: bucket.GetRequeueAfter()}, nil
}
func (r *BucketReconciler) reconcile(ctx context.Context, bucket sourcev1.Bucket) (sourcev1.Bucket, error) {

View File

@ -20,19 +20,14 @@ import (
"context"
"fmt"
"os"
"path/filepath"
"strings"
"time"
securejoin "github.com/cyphar/filepath-securejoin"
"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
kuberecorder "k8s.io/client-go/tools/record"
"k8s.io/client-go/tools/reference"
kerrors "k8s.io/apimachinery/pkg/util/errors"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/client"
@ -41,14 +36,16 @@ import (
"sigs.k8s.io/controller-runtime/pkg/predicate"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/pkg/runtime/conditions"
helper "github.com/fluxcd/pkg/runtime/controller"
"github.com/fluxcd/pkg/runtime/events"
"github.com/fluxcd/pkg/runtime/metrics"
"github.com/fluxcd/pkg/runtime/patch"
"github.com/fluxcd/pkg/runtime/predicates"
"github.com/fluxcd/source-controller/pkg/sourceignore"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/fluxcd/source-controller/pkg/git"
"github.com/fluxcd/source-controller/pkg/git/strategy"
"github.com/fluxcd/source-controller/pkg/sourceignore"
)
// +kubebuilder:rbac:groups=source.toolkit.fluxcd.io,resources=gitrepositories,verbs=get;list;watch;create;update;patch;delete
@ -59,12 +56,12 @@ import (
// GitRepositoryReconciler reconciles a GitRepository object
type GitRepositoryReconciler struct {
client.Client
requeueDependency time.Duration
Scheme *runtime.Scheme
Storage *Storage
EventRecorder kuberecorder.EventRecorder
ExternalEventRecorder *events.Recorder
MetricsRecorder *metrics.Recorder
helper.Events
helper.Metrics
Storage *Storage
requeueDependency time.Duration
}
type GitRepositoryReconcilerOptions struct {
@ -87,410 +84,509 @@ func (r *GitRepositoryReconciler) SetupWithManagerAndOptions(mgr ctrl.Manager, o
Complete(r)
}
func (r *GitRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
func (r *GitRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, retErr error) {
start := time.Now()
log := logr.FromContext(ctx)
log := ctrl.LoggerFrom(ctx)
var repository sourcev1.GitRepository
if err := r.Get(ctx, req.NamespacedName, &repository); err != nil {
// Fetch the GitRepository
obj := &sourcev1.GitRepository{}
if err := r.Get(ctx, req.NamespacedName, obj); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
// Record suspended status metric
defer r.recordSuspension(ctx, repository)
r.RecordSuspend(ctx, obj, obj.Spec.Suspend)
// Add our finalizer if it does not exist
if !controllerutil.ContainsFinalizer(&repository, sourcev1.SourceFinalizer) {
controllerutil.AddFinalizer(&repository, sourcev1.SourceFinalizer)
if err := r.Update(ctx, &repository); err != nil {
log.Error(err, "unable to register finalizer")
return ctrl.Result{}, err
}
}
// Examine if the object is under deletion
if !repository.ObjectMeta.DeletionTimestamp.IsZero() {
return r.reconcileDelete(ctx, repository)
}
// Return early if the object is suspended.
if repository.Spec.Suspend {
// Return early if the object is suspended
if obj.Spec.Suspend {
log.Info("Reconciliation is suspended for this object")
return ctrl.Result{}, nil
}
// check dependencies
if len(repository.Spec.Include) > 0 {
if err := r.checkDependencies(repository); err != nil {
repository = sourcev1.GitRepositoryNotReady(repository, meta.DependencyNotReadyReason, err.Error())
if err := r.updateStatus(ctx, req, repository.Status); err != nil {
log.Error(err, "unable to update status for dependency not ready")
return ctrl.Result{Requeue: true}, err
}
// we can't rely on exponential backoff because it will prolong the execution too much,
// instead we requeue on a fix interval.
msg := fmt.Sprintf("Dependencies do not meet ready condition, retrying in %s", r.requeueDependency.String())
log.Info(msg)
r.event(ctx, repository, events.EventSeverityInfo, msg)
r.recordReadiness(ctx, repository)
return ctrl.Result{RequeueAfter: r.requeueDependency}, nil
}
log.Info("All dependencies area ready, proceeding with reconciliation")
// Initialize the patch helper
patchHelper, err := patch.NewHelper(obj, r.Client)
if err != nil {
return ctrl.Result{}, err
}
// record reconciliation duration
if r.MetricsRecorder != nil {
objRef, err := reference.GetReference(r.Scheme, &repository)
// Always attempt to patch the object and status after each reconciliation
defer func() {
// Record the value of the reconciliation request, if any
if v, ok := meta.ReconcileAnnotationValue(obj.GetAnnotations()); ok {
obj.Status.SetLastHandledReconcileRequest(v)
}
// Summarize the Ready condition based on abnormalities that may have been observed.
conditions.SetSummary(obj,
meta.ReadyCondition,
conditions.WithConditions(
sourcev1.IncludeUnavailableCondition,
sourcev1.SourceVerifiedCondition,
sourcev1.CheckoutFailedCondition,
sourcev1.ArtifactOutdatedCondition,
sourcev1.ArtifactUnavailableCondition,
),
conditions.WithNegativePolarityConditions(
sourcev1.ArtifactUnavailableCondition,
sourcev1.CheckoutFailedCondition,
sourcev1.SourceVerifiedCondition,
sourcev1.IncludeUnavailableCondition,
sourcev1.ArtifactOutdatedCondition,
),
)
// Patch the object, ignoring conflicts on the conditions owned by this controller
patchOpts := []patch.Option{
patch.WithOwnedConditions{
Conditions: []string{
sourcev1.ArtifactUnavailableCondition,
sourcev1.CheckoutFailedCondition,
sourcev1.IncludeUnavailableCondition,
sourcev1.ArtifactOutdatedCondition,
meta.ReadyCondition,
meta.ReconcilingCondition,
meta.StalledCondition,
},
},
}
// Determine if the resource is still being reconciled, or if it has stalled, and record this observation
if retErr == nil && (result.IsZero() || !result.Requeue) {
// We are no longer reconciling
conditions.Delete(obj, meta.ReconcilingCondition)
// We have now observed this generation
patchOpts = append(patchOpts, patch.WithStatusObservedGeneration{})
readyCondition := conditions.Get(obj, meta.ReadyCondition)
switch readyCondition.Status {
case metav1.ConditionFalse:
// As we are no longer reconciling and the end-state is not ready, the reconciliation has stalled
conditions.MarkStalled(obj, readyCondition.Reason, readyCondition.Message)
case metav1.ConditionTrue:
// As we are no longer reconciling and the end-state is ready, the reconciliation is no longer stalled
conditions.Delete(obj, meta.StalledCondition)
}
}
// Finally, patch the resource
if err := patchHelper.Patch(ctx, obj, patchOpts...); err != nil {
retErr = kerrors.NewAggregate([]error{retErr, err})
}
// Always record readiness and duration metrics
r.Metrics.RecordReadiness(ctx, obj)
r.Metrics.RecordDuration(ctx, obj, start)
}()
// Add finalizer first if not exist to avoid the race condition
// between init and delete
if !controllerutil.ContainsFinalizer(obj, sourcev1.SourceFinalizer) {
controllerutil.AddFinalizer(obj, sourcev1.SourceFinalizer)
return ctrl.Result{Requeue: true}, nil
}
// Examine if the object is under deletion
if !obj.ObjectMeta.DeletionTimestamp.IsZero() {
return r.reconcileDelete(ctx, obj)
}
// Reconcile actual object
return r.reconcile(ctx, obj)
}
// reconcile steps through the actual reconciliation tasks for the object, it returns early on the first step that
// produces an error.
func (r *GitRepositoryReconciler) reconcile(ctx context.Context, obj *sourcev1.GitRepository) (ctrl.Result, error) {
// Mark the resource as under reconciliation
conditions.MarkReconciling(obj, meta.ProgressingReason, "")
// Reconcile the storage data
if result, err := r.reconcileStorage(ctx, obj); err != nil || result.IsZero() {
return result, err
}
// Create temp dir for Git clone
tmpDir, err := os.MkdirTemp("", fmt.Sprintf("%s-%s-%s-", obj.Kind, obj.Namespace, obj.Name))
if err != nil {
r.Eventf(ctx, obj, events.EventSeverityError, sourcev1.StorageOperationFailedReason, "Failed to create temporary directory: %s", err)
return ctrl.Result{}, err
}
defer os.RemoveAll(tmpDir)
// Reconcile the source from upstream
var artifact sourcev1.Artifact
if result, err := r.reconcileSource(ctx, obj, &artifact, tmpDir); err != nil || result.IsZero() {
return ctrl.Result{RequeueAfter: obj.GetRequeueAfter()}, err
}
// Reconcile includes from the storage
var includes artifactSet
if result, err := r.reconcileInclude(ctx, obj, includes, tmpDir); err != nil || result.IsZero() {
return ctrl.Result{RequeueAfter: r.requeueDependency}, err
}
// Reconcile the artifact to storage
if result, err := r.reconcileArtifact(ctx, obj, artifact, includes, tmpDir); err != nil || result.IsZero() {
return result, err
}
return ctrl.Result{RequeueAfter: obj.GetRequeueAfter()}, nil
}
// reconcileStorage ensures the current state of the storage matches the desired and previously observed state.
//
// All artifacts for the resource except for the current one are garbage collected from the storage.
// If the artifact in the Status object of the resource disappeared from storage, it is removed from the object.
// If the object does not have an artifact in its Status object, a v1beta1.ArtifactUnavailableCondition is set.
// If the hostname of any of the URLs on the object do not match the current storage server hostname, they are updated.
//
// The caller should assume a failure if an error is returned, or the Result is zero.
func (r *GitRepositoryReconciler) reconcileStorage(ctx context.Context, obj *sourcev1.GitRepository) (ctrl.Result, error) {
// Garbage collect previous advertised artifact(s) from storage
_ = r.garbageCollect(ctx, obj)
// Determine if the advertised artifact is still in storage
if artifact := obj.GetArtifact(); artifact != nil && !r.Storage.ArtifactExist(*artifact) {
obj.Status.Artifact = nil
obj.Status.URL = ""
}
// Record that we do not have an artifact
if obj.GetArtifact() == nil {
conditions.MarkTrue(obj, sourcev1.ArtifactUnavailableCondition, "NoArtifact", "No artifact for resource in storage")
return ctrl.Result{Requeue: true}, nil
}
conditions.Delete(obj, sourcev1.ArtifactUnavailableCondition)
// Always update URLs to ensure hostname is up-to-date
// TODO(hidde): we may want to send out an event only if we notice the URL has changed
r.Storage.SetArtifactURL(obj.GetArtifact())
obj.Status.URL = r.Storage.SetHostname(obj.Status.URL)
return ctrl.Result{RequeueAfter: obj.GetRequeueAfter()}, nil
}
// reconcileSource ensures the upstream Git repository can be reached and checked out using the declared configuration,
// and observes its state.
//
// The repository is checked out to the given dir using the defined configuration, and in case of an error during the
// checkout process (including transient errors), it records v1beta1.CheckoutFailedCondition=True and returns early.
// On a successful checkout it removes v1beta1.CheckoutFailedCondition, and compares the current revision of HEAD to the
// artifact on the object, and records v1beta1.ArtifactOutdatedCondition if they differ.
// If instructed, the signature of the commit is verified if and recorded as v1beta1.SourceVerifiedCondition. If the
// signature can not be verified or the verification fails, the Condition=False and it returns early.
// If both the checkout and signature verification are successful, the given artifact pointer is set to a new artifact
// with the available metadata.
//
// The caller should assume a failure if an error is returned, or the Result is zero.
func (r *GitRepositoryReconciler) reconcileSource(ctx context.Context,
obj *sourcev1.GitRepository, artifact *sourcev1.Artifact, dir string) (ctrl.Result, error) {
// Configure authentication strategy to access the source
auth := &git.Auth{}
if obj.Spec.SecretRef != nil {
// Determine the auth strategy
authStrategy, err := strategy.AuthSecretStrategyForURL(obj.Spec.URL, git.CheckoutOptions{
GitImplementation: obj.Spec.GitImplementation,
RecurseSubmodules: obj.Spec.RecurseSubmodules,
})
if err != nil {
ctrl.LoggerFrom(ctx).Error(err, fmt.Sprintf("Failed to get auth strategy for Git implementation '%s'", obj.Spec.GitImplementation))
conditions.MarkTrue(obj, sourcev1.CheckoutFailedCondition, sourcev1.AuthenticationFailedReason,
"Failed to get auth strategy for Git implementation '%s': %s", obj.Spec.GitImplementation, err)
// Do not return error as recovery without changes is impossible
return ctrl.Result{}, nil
}
// Attempt to retrieve secret
name := types.NamespacedName{
Namespace: obj.GetNamespace(),
Name: obj.Spec.SecretRef.Name,
}
var secret corev1.Secret
if err = r.Client.Get(ctx, name, &secret); err != nil {
conditions.MarkTrue(obj, sourcev1.CheckoutFailedCondition, sourcev1.AuthenticationFailedReason,
"Failed to get secret '%s': %s", name.String(), err.Error())
r.Eventf(ctx, obj, events.EventSeverityError, sourcev1.AuthenticationFailedReason,
"Failed to get secret '%s': %s", name.String(), err.Error())
// Return error as the world as observed may change
return ctrl.Result{}, err
}
defer r.MetricsRecorder.RecordDuration(*objRef, start)
}
// set initial status
if resetRepository, ok := r.resetStatus(repository); ok {
repository = resetRepository
if err := r.updateStatus(ctx, req, repository.Status); err != nil {
log.Error(err, "unable to update status")
return ctrl.Result{Requeue: true}, err
}
r.recordReadiness(ctx, repository)
}
// record the value of the reconciliation request, if any
// TODO(hidde): would be better to defer this in combination with
// always patching the status sub-resource after a reconciliation.
if v, ok := meta.ReconcileAnnotationValue(repository.GetAnnotations()); ok {
repository.Status.SetLastHandledReconcileRequest(v)
}
// purge old artifacts from storage
if err := r.gc(repository); err != nil {
log.Error(err, "unable to purge old artifacts")
}
// reconcile repository by pulling the latest Git commit
reconciledRepository, reconcileErr := r.reconcile(ctx, *repository.DeepCopy())
// update status with the reconciliation result
if err := r.updateStatus(ctx, req, reconciledRepository.Status); err != nil {
log.Error(err, "unable to update status")
return ctrl.Result{Requeue: true}, err
}
// if reconciliation failed, record the failure and requeue immediately
if reconcileErr != nil {
r.event(ctx, reconciledRepository, events.EventSeverityError, reconcileErr.Error())
r.recordReadiness(ctx, reconciledRepository)
return ctrl.Result{Requeue: true}, reconcileErr
}
// emit revision change event
if repository.Status.Artifact == nil || reconciledRepository.Status.Artifact.Revision != repository.Status.Artifact.Revision {
r.event(ctx, reconciledRepository, events.EventSeverityInfo, sourcev1.GitRepositoryReadyMessage(reconciledRepository))
}
r.recordReadiness(ctx, reconciledRepository)
log.Info(fmt.Sprintf("Reconciliation finished in %s, next run in %s",
time.Now().Sub(start).String(),
repository.GetInterval().Duration.String(),
))
return ctrl.Result{RequeueAfter: repository.GetInterval().Duration}, nil
}
func (r *GitRepositoryReconciler) checkDependencies(repository sourcev1.GitRepository) error {
for _, d := range repository.Spec.Include {
dName := types.NamespacedName{Name: d.GitRepositoryRef.Name, Namespace: repository.Namespace}
var gr sourcev1.GitRepository
err := r.Get(context.Background(), dName, &gr)
if err != nil {
return fmt.Errorf("unable to get '%s' dependency: %w", dName, err)
}
if len(gr.Status.Conditions) == 0 || gr.Generation != gr.Status.ObservedGeneration {
return fmt.Errorf("dependency '%s' is not ready", dName)
}
if !apimeta.IsStatusConditionTrue(gr.Status.Conditions, meta.ReadyCondition) {
return fmt.Errorf("dependency '%s' is not ready", dName)
}
}
return nil
}
func (r *GitRepositoryReconciler) reconcile(ctx context.Context, repository sourcev1.GitRepository) (sourcev1.GitRepository, error) {
// create tmp dir for the Git clone
tmpGit, err := os.MkdirTemp("", repository.Name)
if err != nil {
err = fmt.Errorf("tmp dir error: %w", err)
return sourcev1.GitRepositoryNotReady(repository, sourcev1.StorageOperationFailedReason, err.Error()), err
}
defer os.RemoveAll(tmpGit)
// determine auth method
auth := &git.Auth{}
if repository.Spec.SecretRef != nil {
authStrategy, err := strategy.AuthSecretStrategyForURL(
repository.Spec.URL,
git.CheckoutOptions{
GitImplementation: repository.Spec.GitImplementation,
RecurseSubmodules: repository.Spec.RecurseSubmodules,
})
if err != nil {
return sourcev1.GitRepositoryNotReady(repository, sourcev1.AuthenticationFailedReason, err.Error()), err
}
name := types.NamespacedName{
Namespace: repository.GetNamespace(),
Name: repository.Spec.SecretRef.Name,
}
var secret corev1.Secret
err = r.Client.Get(ctx, name, &secret)
if err != nil {
err = fmt.Errorf("auth secret error: %w", err)
return sourcev1.GitRepositoryNotReady(repository, sourcev1.AuthenticationFailedReason, err.Error()), err
}
// Configure strategy with secret
auth, err = authStrategy.Method(secret)
if err != nil {
err = fmt.Errorf("auth error: %w", err)
return sourcev1.GitRepositoryNotReady(repository, sourcev1.AuthenticationFailedReason, err.Error()), err
conditions.MarkTrue(obj, sourcev1.CheckoutFailedCondition, sourcev1.AuthenticationFailedReason,
"Failed to configure auth strategy for Git implementation '%s': %s", obj.Spec.GitImplementation, err)
r.Eventf(ctx, obj, events.EventSeverityError, sourcev1.AuthenticationFailedReason,
"Failed to configure auth strategy for Git implementation '%s': %s", obj.Spec.GitImplementation, err)
// Return error as the contents of the secret may change
return ctrl.Result{}, err
}
}
checkoutStrategy, err := strategy.CheckoutStrategyForRef(
repository.Spec.Reference,
git.CheckoutOptions{
GitImplementation: repository.Spec.GitImplementation,
RecurseSubmodules: repository.Spec.RecurseSubmodules,
},
)
// Configure checkout strategy
checkoutStrategy, err := strategy.CheckoutStrategyForRef(obj.Spec.Reference, git.CheckoutOptions{
GitImplementation: obj.Spec.GitImplementation,
RecurseSubmodules: obj.Spec.RecurseSubmodules,
})
if err != nil {
return sourcev1.GitRepositoryNotReady(repository, sourcev1.GitOperationFailedReason, err.Error()), err
ctrl.LoggerFrom(ctx).Error(err, fmt.Sprintf("Failed to configure checkout strategy for Git implementation '%s'", obj.Spec.GitImplementation))
conditions.MarkTrue(obj, sourcev1.CheckoutFailedCondition, sourcev1.GitOperationFailedReason,
"Failed to configure checkout strategy for Git implementation '%s': %s", obj.Spec.GitImplementation, err)
// Do not return err as recovery without changes is impossible
return ctrl.Result{}, nil
}
gitCtx, cancel := context.WithTimeout(ctx, repository.Spec.Timeout.Duration)
// Checkout HEAD of reference in object
gitCtx, cancel := context.WithTimeout(ctx, obj.Spec.Timeout.Duration)
defer cancel()
commit, revision, err := checkoutStrategy.Checkout(gitCtx, tmpGit, repository.Spec.URL, auth)
commit, revision, err := checkoutStrategy.Checkout(gitCtx, dir, obj.Spec.URL, auth)
if err != nil {
return sourcev1.GitRepositoryNotReady(repository, sourcev1.GitOperationFailedReason, err.Error()), err
conditions.MarkTrue(obj, sourcev1.CheckoutFailedCondition, sourcev1.GitOperationFailedReason,
"Failed to checkout and determine revision: %s", err)
r.Eventf(ctx, obj, events.EventSeverityError, sourcev1.GitOperationFailedReason,
"Failed to checkout and determine revision: %s", err)
// Coin flip on transient or persistent error, return error and hope for the best
return ctrl.Result{}, err
}
r.Eventf(ctx, obj, events.EventSeverityInfo, sourcev1.GitOperationSucceedReason,
"Cloned repository '%s' and checked out revision '%s'", obj.Spec.URL, revision)
conditions.Delete(obj, sourcev1.CheckoutFailedCondition)
// Verify commit signature
if result, err := r.verifyCommitSignature(ctx, obj, commit); err != nil || result.IsZero() {
return result, err
}
artifact := r.Storage.NewArtifactFor(repository.Kind, repository.GetObjectMeta(), revision, fmt.Sprintf("%s.tar.gz", commit.Hash()))
// Create potential new artifact with current available metadata
*artifact = r.Storage.NewArtifactFor(obj.Kind, obj, revision, fmt.Sprintf("%s.tar.gz", commit.Hash()))
// copy all included repository into the artifact
includedArtifacts := []*sourcev1.Artifact{}
for _, incl := range repository.Spec.Include {
dName := types.NamespacedName{Name: incl.GitRepositoryRef.Name, Namespace: repository.Namespace}
var gr sourcev1.GitRepository
err := r.Get(context.Background(), dName, &gr)
if err != nil {
return sourcev1.GitRepositoryNotReady(repository, meta.DependencyNotReadyReason, err.Error()), err
// Mark observations about the revision on the object
if !obj.GetArtifact().HasRevision(revision) {
conditions.MarkTrue(obj, sourcev1.ArtifactOutdatedCondition, "NewRevision", "New upstream revision '%s'", revision)
}
return ctrl.Result{RequeueAfter: obj.GetRequeueAfter()}, nil
}
// reconcileArtifact archives a new artifact to the storage, if the current observation on the object does not match the
// given data.
//
// The inspection of the given data to the object is differed, ensuring any stale observations as
// v1beta1.ArtifactUnavailableCondition and v1beta1.ArtifactOutdatedCondition are always deleted.
// If the given artifact and/or includes do not differ from the object's current, it returns early.
// Source ignore patterns are loaded, and the given directory is archived.
// On a successful archive, the artifact and includes in the status of the given object are set, and the symlink in the
// storage is updated to its path.
//
// The caller should assume a failure if an error is returned, or the Result is zero.
func (r *GitRepositoryReconciler) reconcileArtifact(ctx context.Context, obj *sourcev1.GitRepository, artifact sourcev1.Artifact, includes artifactSet, dir string) (ctrl.Result, error) {
// Always restore the Ready condition in case it got removed due to a transient error
defer func() {
if obj.GetArtifact() != nil {
conditions.Delete(obj, sourcev1.ArtifactUnavailableCondition)
}
includedArtifacts = append(includedArtifacts, gr.GetArtifact())
if obj.GetArtifact().HasRevision(artifact.Revision) && !includes.Diff(obj.Status.IncludedArtifacts) {
conditions.Delete(obj, sourcev1.ArtifactOutdatedCondition)
conditions.MarkTrue(obj, meta.ReadyCondition, meta.SucceededReason,
"Stored artifact for revision '%s'", artifact.Revision)
}
}()
// The artifact is up-to-date
if obj.GetArtifact().HasRevision(artifact.Revision) && !includes.Diff(obj.Status.IncludedArtifacts) {
ctrl.LoggerFrom(ctx).Info(fmt.Sprintf("Already up to date, current revision '%s'", artifact.Revision))
return ctrl.Result{RequeueAfter: obj.GetRequeueAfter()}, nil
}
// return early on unchanged revision and unchanged included repositories
if apimeta.IsStatusConditionTrue(repository.Status.Conditions, meta.ReadyCondition) && repository.GetArtifact().HasRevision(artifact.Revision) && !hasArtifactUpdated(repository.Status.IncludedArtifacts, includedArtifacts) {
if artifact.URL != repository.GetArtifact().URL {
r.Storage.SetArtifactURL(repository.GetArtifact())
repository.Status.URL = r.Storage.SetHostname(repository.Status.URL)
}
return repository, nil
// Ensure target path exists and is a directory
if f, err := os.Stat(dir); err != nil {
ctrl.LoggerFrom(ctx).Error(err, "failed to stat source path")
return ctrl.Result{}, err
} else if !f.IsDir() {
err := fmt.Errorf("source path '%s' is not a directory", dir)
ctrl.LoggerFrom(ctx).Error(err, "invalid target path")
return ctrl.Result{}, err
}
// verify PGP signature
if repository.Spec.Verification != nil {
publicKeySecret := types.NamespacedName{
Namespace: repository.Namespace,
Name: repository.Spec.Verification.SecretRef.Name,
}
var secret corev1.Secret
if err := r.Client.Get(ctx, publicKeySecret, &secret); err != nil {
err = fmt.Errorf("PGP public keys secret error: %w", err)
return sourcev1.GitRepositoryNotReady(repository, sourcev1.VerificationFailedReason, err.Error()), err
}
err := commit.Verify(secret)
if err != nil {
return sourcev1.GitRepositoryNotReady(repository, sourcev1.VerificationFailedReason, err.Error()), err
}
// Ensure artifact directory exists and acquire lock
if err := r.Storage.MkdirAll(artifact); err != nil {
ctrl.LoggerFrom(ctx).Error(err, "failed to create artifact directory")
return ctrl.Result{}, err
}
// create artifact dir
err = r.Storage.MkdirAll(artifact)
if err != nil {
err = fmt.Errorf("mkdir dir error: %w", err)
return sourcev1.GitRepositoryNotReady(repository, sourcev1.StorageOperationFailedReason, err.Error()), err
}
for i, incl := range repository.Spec.Include {
toPath, err := securejoin.SecureJoin(tmpGit, incl.GetToPath())
if err != nil {
return sourcev1.GitRepositoryNotReady(repository, meta.DependencyNotReadyReason, err.Error()), err
}
err = r.Storage.CopyToPath(includedArtifacts[i], incl.GetFromPath(), toPath)
if err != nil {
return sourcev1.GitRepositoryNotReady(repository, meta.DependencyNotReadyReason, err.Error()), err
}
}
// acquire lock
unlock, err := r.Storage.Lock(artifact)
if err != nil {
err = fmt.Errorf("unable to acquire lock: %w", err)
return sourcev1.GitRepositoryNotReady(repository, sourcev1.StorageOperationFailedReason, err.Error()), err
ctrl.LoggerFrom(ctx).Error(err, "failed to acquire lock for artifact")
return ctrl.Result{}, err
}
defer unlock()
// archive artifact and check integrity
ignoreDomain := strings.Split(tmpGit, string(filepath.Separator))
ps, err := sourceignore.LoadIgnorePatterns(tmpGit, ignoreDomain)
// Load ignore rules for archiving
ps, err := sourceignore.LoadIgnorePatterns(dir, nil)
if err != nil {
err = fmt.Errorf(".sourceignore error: %w", err)
return sourcev1.GitRepositoryNotReady(repository, sourcev1.StorageOperationFailedReason, err.Error()), err
r.Eventf(ctx, obj, events.EventSeverityError,
"SourceIgnoreError", "Failed to load source ignore patterns from repository: %s", err)
return ctrl.Result{}, err
}
if repository.Spec.Ignore != nil {
ps = append(ps, sourceignore.ReadPatterns(strings.NewReader(*repository.Spec.Ignore), ignoreDomain)...)
}
if err := r.Storage.Archive(&artifact, tmpGit, SourceIgnoreFilter(ps, ignoreDomain)); err != nil {
err = fmt.Errorf("storage archive error: %w", err)
return sourcev1.GitRepositoryNotReady(repository, sourcev1.StorageOperationFailedReason, err.Error()), err
if obj.Spec.Ignore != nil {
ps = append(ps, sourceignore.ReadPatterns(strings.NewReader(*obj.Spec.Ignore), nil)...)
}
// update latest symlink
// Archive directory to storage
if err := r.Storage.Archive(&artifact, dir, SourceIgnoreFilter(ps, nil)); err != nil {
r.Eventf(ctx, obj, events.EventSeverityError, sourcev1.StorageOperationFailedReason,
"Unable to archive artifact to storage: %s", err)
return ctrl.Result{}, err
}
r.Events.EventWithMetaf(ctx, obj, map[string]string{
"revision": artifact.Revision,
"checksum": artifact.Checksum,
}, events.EventSeverityInfo, "NewArtifact", "Stored artifact for revision '%s'", artifact.Revision)
// Record it on the object
obj.Status.Artifact = artifact.DeepCopy()
obj.Status.IncludedArtifacts = includes
// Update symlink on a "best effort" basis
url, err := r.Storage.Symlink(artifact, "latest.tar.gz")
if err != nil {
err = fmt.Errorf("storage symlink error: %w", err)
return sourcev1.GitRepositoryNotReady(repository, sourcev1.StorageOperationFailedReason, err.Error()), err
r.Events.Eventf(ctx, obj, events.EventSeverityError, sourcev1.StorageOperationFailedReason,
"Failed to update status URL symlink: %s", err)
}
message := fmt.Sprintf("Fetched revision: %s", artifact.Revision)
return sourcev1.GitRepositoryReady(repository, artifact, includedArtifacts, url, sourcev1.GitOperationSucceedReason, message), nil
if url != "" {
obj.Status.URL = url
}
return ctrl.Result{RequeueAfter: obj.GetRequeueAfter()}, nil
}
func (r *GitRepositoryReconciler) reconcileDelete(ctx context.Context, repository sourcev1.GitRepository) (ctrl.Result, error) {
if err := r.gc(repository); err != nil {
r.event(ctx, repository, events.EventSeverityError,
fmt.Sprintf("garbage collection for deleted resource failed: %s", err.Error()))
// reconcileInclude reconciles the declared includes from the object by copying their artifact (sub)contents to the
// declared paths in the given directory.
//
// If an include is unavailable, it marks the object with v1beta1.IncludeUnavailableCondition and returns early.
// If the copy operations are successful, it deletes the v1beta1.IncludeUnavailableCondition from the object.
// If the artifactSet differs from the current set, it marks the object with v1beta1.ArtifactOutdatedCondition.
//
// The caller should assume a failure if an error is returned, or the Result is zero.
func (r *GitRepositoryReconciler) reconcileInclude(ctx context.Context, obj *sourcev1.GitRepository, artifacts artifactSet, dir string) (ctrl.Result, error) {
artifacts = make(artifactSet, len(obj.Spec.Include))
for i, incl := range obj.Spec.Include {
// Do this first as it is much cheaper than copy operations
toPath, err := securejoin.SecureJoin(dir, incl.GetToPath())
if err != nil {
conditions.MarkTrue(obj, sourcev1.IncludeUnavailableCondition, "IllegalPath",
"Path calculation for include '%s' failed: %s", incl.GitRepositoryRef.Name, err.Error())
return ctrl.Result{}, err
}
// Retrieve the included GitRepository
dep := &sourcev1.GitRepository{}
if err := r.Get(ctx, types.NamespacedName{Namespace: obj.Namespace, Name: incl.GitRepositoryRef.Name}, dep); err != nil {
conditions.MarkTrue(obj, sourcev1.IncludeUnavailableCondition, "NotFound",
"Could not get resource for include '%s': %s", incl.GitRepositoryRef.Name, err.Error())
return ctrl.Result{}, err
}
// Confirm include has an artifact
if dep.GetArtifact() == nil {
ctrl.LoggerFrom(ctx).Error(nil, fmt.Sprintf("No artifact available for include '%s'", incl.GitRepositoryRef.Name))
conditions.MarkTrue(obj, sourcev1.IncludeUnavailableCondition, "NoArtifact",
"No artifact available for include '%s'", incl.GitRepositoryRef.Name)
return ctrl.Result{}, nil
}
// Copy artifact (sub)contents to configured directory
if err := r.Storage.CopyToPath(dep.GetArtifact(), incl.GetFromPath(), toPath); err != nil {
conditions.MarkTrue(obj, sourcev1.IncludeUnavailableCondition, "CopyFailure",
"Failed to copy '%s' include from %s to %s: %s", incl.GitRepositoryRef.Name, incl.GetFromPath(), incl.GetToPath(), err.Error())
r.Eventf(ctx, obj, events.EventSeverityError, sourcev1.IncludeUnavailableCondition,
"Failed to copy '%s' include from %s to %s: %s", incl.GitRepositoryRef.Name, incl.GetFromPath(), incl.GetToPath(), err.Error())
return ctrl.Result{}, err
}
artifacts[i] = dep.GetArtifact().DeepCopy()
}
// We now know all includes are available
conditions.Delete(obj, sourcev1.IncludeUnavailableCondition)
// Observe if the artifacts still match the previous included ones
if artifacts.Diff(obj.Status.IncludedArtifacts) {
conditions.MarkTrue(obj, sourcev1.ArtifactOutdatedCondition, "IncludeChange", "Included artifacts differ from last observed includes")
}
return ctrl.Result{RequeueAfter: obj.GetRequeueAfter()}, nil
}
// reconcileDelete handles the delete of an object. It first garbage collects all artifacts for the object from the
// artifact storage, if successful, the finalizer is removed from the object.
func (r *GitRepositoryReconciler) reconcileDelete(ctx context.Context, obj *sourcev1.GitRepository) (ctrl.Result, error) {
// Garbage collect the resource's artifacts
if err := r.garbageCollect(ctx, obj); err != nil {
// Return the error so we retry the failed garbage collection
return ctrl.Result{}, err
}
// Record deleted status
r.recordReadiness(ctx, repository)
// Remove our finalizer from the list and update it
controllerutil.RemoveFinalizer(&repository, sourcev1.SourceFinalizer)
if err := r.Update(ctx, &repository); err != nil {
return ctrl.Result{}, err
}
// Remove our finalizer from the list
controllerutil.RemoveFinalizer(obj, sourcev1.SourceFinalizer)
// Stop reconciliation as the object is being deleted
return ctrl.Result{}, nil
}
// resetStatus returns a modified v1beta1.GitRepository and a boolean indicating
// if the status field has been reset.
func (r *GitRepositoryReconciler) resetStatus(repository sourcev1.GitRepository) (sourcev1.GitRepository, bool) {
// We do not have an artifact, or it does no longer exist
if repository.GetArtifact() == nil || !r.Storage.ArtifactExist(*repository.GetArtifact()) {
repository = sourcev1.GitRepositoryProgressing(repository)
repository.Status.Artifact = nil
return repository, true
// verifyCommitSignature verifies the signature of the given commit if a verification mode is configured on the object.
func (r *GitRepositoryReconciler) verifyCommitSignature(ctx context.Context, obj *sourcev1.GitRepository, commit git.Commit) (ctrl.Result, error) {
// Check if there is a commit verification is configured and remove any old observations if there is none
if obj.Spec.Verification == nil || obj.Spec.Verification.Mode == "" {
conditions.Delete(obj, sourcev1.SourceVerifiedCondition)
return ctrl.Result{RequeueAfter: obj.GetRequeueAfter()}, nil
}
if repository.Generation != repository.Status.ObservedGeneration {
return sourcev1.GitRepositoryProgressing(repository), true
// Get secret with GPG data
publicKeySecret := types.NamespacedName{
Namespace: obj.Namespace,
Name: obj.Spec.Verification.SecretRef.Name,
}
return repository, false
var secret corev1.Secret
if err := r.Client.Get(ctx, publicKeySecret, &secret); err != nil {
conditions.MarkFalse(obj, sourcev1.SourceVerifiedCondition, meta.FailedReason, "PGP public keys secret error: %s", err.Error())
r.Eventf(ctx, obj, events.EventSeverityError, "VerificationError", "PGP public keys secret error: %s", err.Error())
return ctrl.Result{}, err
}
// Verify commit with GPG data from secret
if err := commit.Verify(secret); err != nil {
conditions.MarkFalse(obj, sourcev1.SourceVerifiedCondition, meta.FailedReason, "Signature verification of commit '%s' failed: %s", commit.Hash(), err)
r.Eventf(ctx, obj, events.EventSeverityError, "InvalidCommitSignature", "Signature verification of commit '%s' failed: %s", commit.Hash(), err)
// Return error in the hope the secret changes
return ctrl.Result{}, err
}
conditions.MarkTrue(obj, sourcev1.SourceVerifiedCondition, meta.SucceededReason, "Verified signature of commit '%s'", commit.Hash())
r.Eventf(ctx, obj, events.EventSeverityInfo, "VerifiedCommit", "Verified signature of commit '%s'", commit.Hash())
return ctrl.Result{RequeueAfter: obj.GetRequeueAfter()}, nil
}
// gc performs a garbage collection for the given v1beta1.GitRepository.
// It removes all but the current artifact except for when the
// deletion timestamp is set, which will result in the removal of
// all artifacts for the resource.
func (r *GitRepositoryReconciler) gc(repository sourcev1.GitRepository) error {
if !repository.DeletionTimestamp.IsZero() {
return r.Storage.RemoveAll(r.Storage.NewArtifactFor(repository.Kind, repository.GetObjectMeta(), "", "*"))
// garbageCollect performs a garbage collection for the given v1beta1.GitRepository. It removes all but the current
// artifact except for when the deletion timestamp is set, which will result in the removal of all artifacts for the
// resource.
func (r *GitRepositoryReconciler) garbageCollect(ctx context.Context, obj *sourcev1.GitRepository) error {
if !obj.DeletionTimestamp.IsZero() {
if err := r.Storage.RemoveAll(r.Storage.NewArtifactFor(obj.Kind, obj.GetObjectMeta(), "", "*")); err != nil {
r.Eventf(ctx, obj, events.EventSeverityError, "GarbageCollectionFailed",
"Garbage collection for deleted resource failed: %s", err)
return err
}
obj.Status.Artifact = nil
// TODO(hidde): we should only push this event if we actually garbage collected something
r.Eventf(ctx, obj, events.EventSeverityInfo, "GarbageCollectionSucceeded",
"Garbage collected artifacts for deleted resource")
return nil
}
if repository.GetArtifact() != nil {
return r.Storage.RemoveAllButCurrent(*repository.GetArtifact())
if obj.GetArtifact() != nil {
if err := r.Storage.RemoveAllButCurrent(*obj.GetArtifact()); err != nil {
r.Eventf(ctx, obj, events.EventSeverityError, "GarbageCollectionFailed", "Garbage collection of old artifacts failed: %s", err)
return err
}
// TODO(hidde): we should only push this event if we actually garbage collected something
r.Eventf(ctx, obj, events.EventSeverityInfo, "GarbageCollectionSucceeded", "Garbage collected old artifacts")
}
return nil
}
// event emits a Kubernetes event and forwards the event to notification controller if configured
func (r *GitRepositoryReconciler) event(ctx context.Context, repository sourcev1.GitRepository, severity, msg string) {
log := logr.FromContext(ctx)
if r.EventRecorder != nil {
r.EventRecorder.Eventf(&repository, "Normal", severity, msg)
}
if r.ExternalEventRecorder != nil {
objRef, err := reference.GetReference(r.Scheme, &repository)
if err != nil {
log.Error(err, "unable to send event")
return
}
if err := r.ExternalEventRecorder.Eventf(*objRef, nil, severity, severity, msg); err != nil {
log.Error(err, "unable to send event")
return
}
}
}
func (r *GitRepositoryReconciler) recordReadiness(ctx context.Context, repository sourcev1.GitRepository) {
log := logr.FromContext(ctx)
if r.MetricsRecorder == nil {
return
}
objRef, err := reference.GetReference(r.Scheme, &repository)
if err != nil {
log.Error(err, "unable to record readiness metric")
return
}
if rc := apimeta.FindStatusCondition(repository.Status.Conditions, meta.ReadyCondition); rc != nil {
r.MetricsRecorder.RecordCondition(*objRef, *rc, !repository.DeletionTimestamp.IsZero())
} else {
r.MetricsRecorder.RecordCondition(*objRef, metav1.Condition{
Type: meta.ReadyCondition,
Status: metav1.ConditionUnknown,
}, !repository.DeletionTimestamp.IsZero())
}
}
func (r *GitRepositoryReconciler) recordSuspension(ctx context.Context, gitrepository sourcev1.GitRepository) {
if r.MetricsRecorder == nil {
return
}
log := logr.FromContext(ctx)
objRef, err := reference.GetReference(r.Scheme, &gitrepository)
if err != nil {
log.Error(err, "unable to record suspended metric")
return
}
if !gitrepository.DeletionTimestamp.IsZero() {
r.MetricsRecorder.RecordSuspend(*objRef, false)
} else {
r.MetricsRecorder.RecordSuspend(*objRef, gitrepository.Spec.Suspend)
}
}
func (r *GitRepositoryReconciler) updateStatus(ctx context.Context, req ctrl.Request, newStatus sourcev1.GitRepositoryStatus) error {
var repository sourcev1.GitRepository
if err := r.Get(ctx, req.NamespacedName, &repository); err != nil {
return err
}
patch := client.MergeFrom(repository.DeepCopy())
repository.Status = newStatus
return r.Status().Patch(ctx, &repository, patch)
}

File diff suppressed because it is too large Load Diff

View File

@ -252,9 +252,9 @@ func (r *HelmChartReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
log.Info(fmt.Sprintf("Reconciliation finished in %s, next run in %s",
time.Now().Sub(start).String(),
chart.GetInterval().Duration.String(),
chart.GetRequeueAfter().String(),
))
return ctrl.Result{RequeueAfter: chart.GetInterval().Duration}, nil
return ctrl.Result{RequeueAfter: chart.GetRequeueAfter()}, nil
}
type HelmChartReconcilerOptions struct {

View File

@ -130,9 +130,9 @@ var _ = Describe("HelmChartReconciler", func() {
got := &sourcev1.HelmChart{}
Eventually(func() bool {
_ = k8sClient.Get(context.Background(), key, got)
return got.Status.Artifact != nil && storage.ArtifactExist(*got.Status.Artifact)
return got.Status.Artifact != nil && ginkgoTestStorage.ArtifactExist(*got.Status.Artifact)
}, timeout, interval).Should(BeTrue())
helmChart, err := loader.Load(storage.LocalPath(*got.Status.Artifact))
helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact))
Expect(err).NotTo(HaveOccurred())
Expect(helmChart.Values["testDefault"]).To(BeTrue())
Expect(helmChart.Values["testOverride"]).To(BeFalse())
@ -147,7 +147,7 @@ var _ = Describe("HelmChartReconciler", func() {
_ = k8sClient.Get(context.Background(), key, now)
// Test revision change and garbage collection
return now.Status.Artifact.Revision != got.Status.Artifact.Revision &&
!storage.ArtifactExist(*got.Status.Artifact)
!ginkgoTestStorage.ArtifactExist(*got.Status.Artifact)
}, timeout, interval).Should(BeTrue())
When("Setting valid valuesFiles attribute", func() {
@ -162,12 +162,12 @@ var _ = Describe("HelmChartReconciler", func() {
Eventually(func() bool {
_ = k8sClient.Get(context.Background(), key, got)
return got.Status.Artifact.Checksum != updated.Status.Artifact.Checksum &&
storage.ArtifactExist(*got.Status.Artifact)
ginkgoTestStorage.ArtifactExist(*got.Status.Artifact)
}, timeout, interval).Should(BeTrue())
f, err := os.Stat(storage.LocalPath(*got.Status.Artifact))
f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact))
Expect(err).NotTo(HaveOccurred())
Expect(f.Size()).To(BeNumerically(">", 0))
helmChart, err := loader.Load(storage.LocalPath(*got.Status.Artifact))
helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact))
Expect(err).NotTo(HaveOccurred())
Expect(helmChart.Values["testDefault"]).To(BeTrue())
Expect(helmChart.Values["testOverride"]).To(BeTrue())
@ -185,12 +185,12 @@ var _ = Describe("HelmChartReconciler", func() {
Eventually(func() bool {
_ = k8sClient.Get(context.Background(), key, got)
return got.Status.ObservedGeneration > updated.Status.ObservedGeneration &&
storage.ArtifactExist(*got.Status.Artifact)
ginkgoTestStorage.ArtifactExist(*got.Status.Artifact)
}, timeout, interval).Should(BeTrue())
f, err := os.Stat(storage.LocalPath(*got.Status.Artifact))
f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact))
Expect(err).NotTo(HaveOccurred())
Expect(f.Size()).To(BeNumerically(">", 0))
helmChart, err := loader.Load(storage.LocalPath(*got.Status.Artifact))
helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact))
Expect(err).NotTo(HaveOccurred())
Expect(helmChart.Values["testDefault"]).To(BeTrue())
Expect(helmChart.Values["testOverride"]).To(BeTrue())
@ -208,12 +208,12 @@ var _ = Describe("HelmChartReconciler", func() {
Eventually(func() bool {
_ = k8sClient.Get(context.Background(), key, got)
return got.Status.Artifact.Checksum != updated.Status.Artifact.Checksum &&
storage.ArtifactExist(*got.Status.Artifact)
ginkgoTestStorage.ArtifactExist(*got.Status.Artifact)
}, timeout, interval).Should(BeTrue())
f, err := os.Stat(storage.LocalPath(*got.Status.Artifact))
f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact))
Expect(err).NotTo(HaveOccurred())
Expect(f.Size()).To(BeNumerically(">", 0))
helmChart, err := loader.Load(storage.LocalPath(*got.Status.Artifact))
helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact))
Expect(err).NotTo(HaveOccurred())
Expect(helmChart.Values["testDefault"]).To(BeTrue())
Expect(helmChart.Values["testOverride"]).To(BeTrue())
@ -229,12 +229,12 @@ var _ = Describe("HelmChartReconciler", func() {
Eventually(func() bool {
_ = k8sClient.Get(context.Background(), key, got)
return got.Status.Artifact.Checksum != updated.Status.Artifact.Checksum &&
storage.ArtifactExist(*got.Status.Artifact)
ginkgoTestStorage.ArtifactExist(*got.Status.Artifact)
}, timeout, interval).Should(BeTrue())
f, err := os.Stat(storage.LocalPath(*got.Status.Artifact))
f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact))
Expect(err).NotTo(HaveOccurred())
Expect(f.Size()).To(BeNumerically(">", 0))
helmChart, err := loader.Load(storage.LocalPath(*got.Status.Artifact))
helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact))
Expect(err).NotTo(HaveOccurred())
_, exists := helmChart.Values["testDefault"]
Expect(exists).To(BeFalse())
@ -251,12 +251,12 @@ var _ = Describe("HelmChartReconciler", func() {
Eventually(func() bool {
_ = k8sClient.Get(context.Background(), key, got)
return got.Status.Artifact.Checksum != updated.Status.Artifact.Checksum &&
storage.ArtifactExist(*got.Status.Artifact)
ginkgoTestStorage.ArtifactExist(*got.Status.Artifact)
}, timeout, interval).Should(BeTrue())
f, err := os.Stat(storage.LocalPath(*got.Status.Artifact))
f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact))
Expect(err).NotTo(HaveOccurred())
Expect(f.Size()).To(BeNumerically(">", 0))
helmChart, err := loader.Load(storage.LocalPath(*got.Status.Artifact))
helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact))
Expect(err).NotTo(HaveOccurred())
Expect(helmChart.Values["testDefault"]).To(BeTrue())
Expect(helmChart.Values["testOverride"]).To(BeFalse())
@ -272,12 +272,12 @@ var _ = Describe("HelmChartReconciler", func() {
Eventually(func() bool {
_ = k8sClient.Get(context.Background(), key, got)
return got.Status.ObservedGeneration > updated.Status.ObservedGeneration &&
storage.ArtifactExist(*got.Status.Artifact)
ginkgoTestStorage.ArtifactExist(*got.Status.Artifact)
}, timeout, interval).Should(BeTrue())
f, err := os.Stat(storage.LocalPath(*got.Status.Artifact))
f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact))
Expect(err).NotTo(HaveOccurred())
Expect(f.Size()).To(BeNumerically(">", 0))
helmChart, err := loader.Load(storage.LocalPath(*got.Status.Artifact))
helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact))
Expect(err).NotTo(HaveOccurred())
Expect(helmChart.Values["testDefault"]).To(BeTrue())
Expect(helmChart.Values["testOverride"]).To(BeFalse())
@ -683,7 +683,7 @@ var _ = Describe("HelmChartReconciler", func() {
Eventually(func() bool {
_ = k8sClient.Get(context.Background(), key, got)
return got.Status.Artifact != nil &&
storage.ArtifactExist(*got.Status.Artifact)
ginkgoTestStorage.ArtifactExist(*got.Status.Artifact)
}, timeout, interval).Should(BeTrue())
By("Committing a new version in the chart metadata")
@ -728,9 +728,9 @@ var _ = Describe("HelmChartReconciler", func() {
_ = k8sClient.Get(context.Background(), key, now)
// Test revision change and garbage collection
return now.Status.Artifact.Revision != got.Status.Artifact.Revision &&
!storage.ArtifactExist(*got.Status.Artifact)
!ginkgoTestStorage.ArtifactExist(*got.Status.Artifact)
}, timeout, interval).Should(BeTrue())
helmChart, err := loader.Load(storage.LocalPath(*now.Status.Artifact))
helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*now.Status.Artifact))
Expect(err).NotTo(HaveOccurred())
Expect(helmChart.Values["testDefault"]).To(BeTrue())
Expect(helmChart.Values["testOverride"]).To(BeFalse())
@ -747,12 +747,12 @@ var _ = Describe("HelmChartReconciler", func() {
Eventually(func() bool {
_ = k8sClient.Get(context.Background(), key, got)
return got.Status.Artifact.Checksum != updated.Status.Artifact.Checksum &&
storage.ArtifactExist(*got.Status.Artifact)
ginkgoTestStorage.ArtifactExist(*got.Status.Artifact)
}, timeout, interval).Should(BeTrue())
f, err := os.Stat(storage.LocalPath(*got.Status.Artifact))
f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact))
Expect(err).NotTo(HaveOccurred())
Expect(f.Size()).To(BeNumerically(">", 0))
helmChart, err := loader.Load(storage.LocalPath(*got.Status.Artifact))
helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact))
Expect(err).NotTo(HaveOccurred())
Expect(helmChart.Values["testDefault"]).To(BeTrue())
Expect(helmChart.Values["testOverride"]).To(BeTrue())
@ -770,12 +770,12 @@ var _ = Describe("HelmChartReconciler", func() {
Eventually(func() bool {
_ = k8sClient.Get(context.Background(), key, got)
return got.Status.ObservedGeneration > updated.Status.ObservedGeneration &&
storage.ArtifactExist(*got.Status.Artifact)
ginkgoTestStorage.ArtifactExist(*got.Status.Artifact)
}, timeout, interval).Should(BeTrue())
f, err := os.Stat(storage.LocalPath(*got.Status.Artifact))
f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact))
Expect(err).NotTo(HaveOccurred())
Expect(f.Size()).To(BeNumerically(">", 0))
helmChart, err := loader.Load(storage.LocalPath(*got.Status.Artifact))
helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact))
Expect(err).NotTo(HaveOccurred())
Expect(helmChart.Values["testDefault"]).To(BeTrue())
Expect(helmChart.Values["testOverride"]).To(BeTrue())
@ -793,12 +793,12 @@ var _ = Describe("HelmChartReconciler", func() {
Eventually(func() bool {
_ = k8sClient.Get(context.Background(), key, got)
return got.Status.Artifact.Checksum != updated.Status.Artifact.Checksum &&
storage.ArtifactExist(*got.Status.Artifact)
ginkgoTestStorage.ArtifactExist(*got.Status.Artifact)
}, timeout, interval).Should(BeTrue())
f, err := os.Stat(storage.LocalPath(*got.Status.Artifact))
f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact))
Expect(err).NotTo(HaveOccurred())
Expect(f.Size()).To(BeNumerically(">", 0))
helmChart, err := loader.Load(storage.LocalPath(*got.Status.Artifact))
helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact))
Expect(err).NotTo(HaveOccurred())
Expect(helmChart.Values["testDefault"]).To(BeTrue())
Expect(helmChart.Values["testOverride"]).To(BeTrue())
@ -814,12 +814,12 @@ var _ = Describe("HelmChartReconciler", func() {
Eventually(func() bool {
_ = k8sClient.Get(context.Background(), key, got)
return got.Status.Artifact.Checksum != updated.Status.Artifact.Checksum &&
storage.ArtifactExist(*got.Status.Artifact)
ginkgoTestStorage.ArtifactExist(*got.Status.Artifact)
}, timeout, interval).Should(BeTrue())
f, err := os.Stat(storage.LocalPath(*got.Status.Artifact))
f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact))
Expect(err).NotTo(HaveOccurred())
Expect(f.Size()).To(BeNumerically(">", 0))
helmChart, err := loader.Load(storage.LocalPath(*got.Status.Artifact))
helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact))
Expect(err).NotTo(HaveOccurred())
_, exists := helmChart.Values["testDefault"]
Expect(exists).To(BeFalse())
@ -836,12 +836,12 @@ var _ = Describe("HelmChartReconciler", func() {
Eventually(func() bool {
_ = k8sClient.Get(context.Background(), key, got)
return got.Status.ObservedGeneration > updated.Status.ObservedGeneration &&
storage.ArtifactExist(*got.Status.Artifact)
ginkgoTestStorage.ArtifactExist(*got.Status.Artifact)
}, timeout, interval).Should(BeTrue())
f, err := os.Stat(storage.LocalPath(*got.Status.Artifact))
f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact))
Expect(err).NotTo(HaveOccurred())
Expect(f.Size()).To(BeNumerically(">", 0))
helmChart, err := loader.Load(storage.LocalPath(*got.Status.Artifact))
helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact))
Expect(err).NotTo(HaveOccurred())
_, exists := helmChart.Values["testDefault"]
Expect(exists).To(BeFalse())
@ -946,7 +946,7 @@ var _ = Describe("HelmChartReconciler", func() {
Eventually(func() bool {
_ = k8sClient.Get(context.Background(), key, got)
return got.Status.Artifact != nil &&
storage.ArtifactExist(*got.Status.Artifact)
ginkgoTestStorage.ArtifactExist(*got.Status.Artifact)
}, timeout, interval).Should(BeTrue())
})
})
@ -1189,9 +1189,9 @@ var _ = Describe("HelmChartReconciler", func() {
Eventually(func() bool {
_ = k8sClient.Get(context.Background(), key, got)
return got.Status.Artifact != nil &&
storage.ArtifactExist(*got.Status.Artifact)
ginkgoTestStorage.ArtifactExist(*got.Status.Artifact)
}, timeout, interval).Should(BeTrue())
helmChart, err := loader.Load(storage.LocalPath(*got.Status.Artifact))
helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact))
Expect(err).NotTo(HaveOccurred())
Expect(helmChart.Values["testDefault"]).To(BeTrue())
Expect(helmChart.Values["testOverride"]).To(BeFalse())
@ -1208,12 +1208,12 @@ var _ = Describe("HelmChartReconciler", func() {
Eventually(func() bool {
_ = k8sClient.Get(context.Background(), key, got)
return got.Status.Artifact.Checksum != updated.Status.Artifact.Checksum &&
storage.ArtifactExist(*got.Status.Artifact)
ginkgoTestStorage.ArtifactExist(*got.Status.Artifact)
}, timeout, interval).Should(BeTrue())
f, err := os.Stat(storage.LocalPath(*got.Status.Artifact))
f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact))
Expect(err).NotTo(HaveOccurred())
Expect(f.Size()).To(BeNumerically(">", 0))
helmChart, err := loader.Load(storage.LocalPath(*got.Status.Artifact))
helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact))
Expect(err).NotTo(HaveOccurred())
Expect(helmChart.Values["testDefault"]).To(BeTrue())
Expect(helmChart.Values["testOverride"]).To(BeTrue())
@ -1231,12 +1231,12 @@ var _ = Describe("HelmChartReconciler", func() {
Eventually(func() bool {
_ = k8sClient.Get(context.Background(), key, got)
return got.Status.ObservedGeneration > updated.Status.ObservedGeneration &&
storage.ArtifactExist(*got.Status.Artifact)
ginkgoTestStorage.ArtifactExist(*got.Status.Artifact)
}, timeout, interval).Should(BeTrue())
f, err := os.Stat(storage.LocalPath(*got.Status.Artifact))
f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact))
Expect(err).NotTo(HaveOccurred())
Expect(f.Size()).To(BeNumerically(">", 0))
helmChart, err := loader.Load(storage.LocalPath(*got.Status.Artifact))
helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact))
Expect(err).NotTo(HaveOccurred())
Expect(helmChart.Values["testDefault"]).To(BeTrue())
Expect(helmChart.Values["testOverride"]).To(BeTrue())
@ -1254,12 +1254,12 @@ var _ = Describe("HelmChartReconciler", func() {
Eventually(func() bool {
_ = k8sClient.Get(context.Background(), key, got)
return got.Status.Artifact.Checksum != updated.Status.Artifact.Checksum &&
storage.ArtifactExist(*got.Status.Artifact)
ginkgoTestStorage.ArtifactExist(*got.Status.Artifact)
}, timeout, interval).Should(BeTrue())
f, err := os.Stat(storage.LocalPath(*got.Status.Artifact))
f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact))
Expect(err).NotTo(HaveOccurred())
Expect(f.Size()).To(BeNumerically(">", 0))
helmChart, err := loader.Load(storage.LocalPath(*got.Status.Artifact))
helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact))
Expect(err).NotTo(HaveOccurred())
Expect(helmChart.Values["testDefault"]).To(BeTrue())
Expect(helmChart.Values["testOverride"]).To(BeTrue())
@ -1275,12 +1275,12 @@ var _ = Describe("HelmChartReconciler", func() {
Eventually(func() bool {
_ = k8sClient.Get(context.Background(), key, got)
return got.Status.Artifact.Checksum != updated.Status.Artifact.Checksum &&
storage.ArtifactExist(*got.Status.Artifact)
ginkgoTestStorage.ArtifactExist(*got.Status.Artifact)
}, timeout, interval).Should(BeTrue())
f, err := os.Stat(storage.LocalPath(*got.Status.Artifact))
f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact))
Expect(err).NotTo(HaveOccurred())
Expect(f.Size()).To(BeNumerically(">", 0))
helmChart, err := loader.Load(storage.LocalPath(*got.Status.Artifact))
helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact))
Expect(err).NotTo(HaveOccurred())
_, exists := helmChart.Values["testDefault"]
Expect(exists).To(BeFalse())
@ -1297,12 +1297,12 @@ var _ = Describe("HelmChartReconciler", func() {
Eventually(func() bool {
_ = k8sClient.Get(context.Background(), key, got)
return got.Status.ObservedGeneration > updated.Status.ObservedGeneration &&
storage.ArtifactExist(*got.Status.Artifact)
ginkgoTestStorage.ArtifactExist(*got.Status.Artifact)
}, timeout, interval).Should(BeTrue())
f, err := os.Stat(storage.LocalPath(*got.Status.Artifact))
f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact))
Expect(err).NotTo(HaveOccurred())
Expect(f.Size()).To(BeNumerically(">", 0))
helmChart, err := loader.Load(storage.LocalPath(*got.Status.Artifact))
helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact))
Expect(err).NotTo(HaveOccurred())
_, exists := helmChart.Values["testDefault"]
Expect(exists).To(BeFalse())

View File

@ -164,10 +164,10 @@ func (r *HelmRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Reque
log.Info(fmt.Sprintf("Reconciliation finished in %s, next run in %s",
time.Now().Sub(start).String(),
repository.GetInterval().Duration.String(),
repository.GetRequeueAfter().String(),
))
return ctrl.Result{RequeueAfter: repository.GetInterval().Duration}, nil
return ctrl.Result{RequeueAfter: repository.GetRequeueAfter()}, nil
}
func (r *HelmRepositoryReconciler) reconcile(ctx context.Context, repository sourcev1.HelmRepository) (sourcev1.HelmRepository, error) {

View File

@ -99,7 +99,7 @@ var _ = Describe("HelmRepositoryReconciler", func() {
got := &sourcev1.HelmRepository{}
Eventually(func() bool {
_ = k8sClient.Get(context.Background(), key, got)
return got.Status.Artifact != nil && storage.ArtifactExist(*got.Status.Artifact)
return got.Status.Artifact != nil && ginkgoTestStorage.ArtifactExist(*got.Status.Artifact)
}, timeout, interval).Should(BeTrue())
By("Updating the chart index")
@ -112,7 +112,7 @@ var _ = Describe("HelmRepositoryReconciler", func() {
_ = k8sClient.Get(context.Background(), key, now)
// Test revision change and garbage collection
return now.Status.Artifact.Revision != got.Status.Artifact.Revision &&
!storage.ArtifactExist(*got.Status.Artifact)
!ginkgoTestStorage.ArtifactExist(*got.Status.Artifact)
}, timeout, interval).Should(BeTrue())
updated := &sourcev1.HelmRepository{}
@ -296,7 +296,7 @@ var _ = Describe("HelmRepositoryReconciler", func() {
got := &sourcev1.HelmRepository{}
_ = k8sClient.Get(context.Background(), key, got)
return got.Status.Artifact != nil &&
storage.ArtifactExist(*got.Status.Artifact)
ginkgoTestStorage.ArtifactExist(*got.Status.Artifact)
}, timeout, interval).Should(BeTrue())
By("Expecting missing secret error")
@ -390,7 +390,7 @@ var _ = Describe("HelmRepositoryReconciler", func() {
got := &sourcev1.HelmRepository{}
_ = k8sClient.Get(context.Background(), key, got)
return got.Status.Artifact != nil &&
storage.ArtifactExist(*got.Status.Artifact)
ginkgoTestStorage.ArtifactExist(*got.Status.Artifact)
}, timeout, interval).Should(BeTrue())
By("Expecting missing secret error")

View File

@ -0,0 +1,193 @@
/*
Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package controllers
import (
"math/rand"
"net/http"
"os"
"path/filepath"
"testing"
"time"
"github.com/fluxcd/pkg/runtime/controller"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"helm.sh/helm/v3/pkg/getter"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/envtest"
"sigs.k8s.io/controller-runtime/pkg/envtest/printer"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
// +kubebuilder:scaffold:imports
)
// These tests use Ginkgo (BDD-style Go testing framework). Refer to
// http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
var cfg *rest.Config
var k8sClient client.Client
var k8sManager ctrl.Manager
var ginkgoTestEnv *envtest.Environment
var ginkgoTestStorage *Storage
var examplePublicKey []byte
var examplePrivateKey []byte
var exampleCA []byte
func TestAPIs(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecsWithDefaultAndCustomReporters(t,
"Controller Suite",
[]Reporter{printer.NewlineReporter{}})
}
var _ = BeforeSuite(func(done Done) {
logf.SetLogger(
zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)),
)
By("bootstrapping test environment")
t := true
if os.Getenv("TEST_USE_EXISTING_CLUSTER") == "true" {
ginkgoTestEnv = &envtest.Environment{
UseExistingCluster: &t,
}
} else {
ginkgoTestEnv = &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")},
}
}
var err error
cfg, err = ginkgoTestEnv.Start()
Expect(err).ToNot(HaveOccurred())
Expect(cfg).ToNot(BeNil())
err = sourcev1.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
err = sourcev1.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
err = sourcev1.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
// +kubebuilder:scaffold:scheme
Expect(loadExampleKeys()).To(Succeed())
tmpStoragePath, err := os.MkdirTemp("", "source-controller-storage-")
Expect(err).NotTo(HaveOccurred(), "failed to create tmp storage dir")
ginkgoTestStorage, err = NewStorage(tmpStoragePath, "localhost:5050", time.Second*30)
Expect(err).NotTo(HaveOccurred(), "failed to create tmp storage")
// serve artifacts from the filesystem, as done in main.go
fs := http.FileServer(http.Dir(tmpStoragePath))
http.Handle("/", fs)
go http.ListenAndServe(":5050", nil)
k8sManager, err = ctrl.NewManager(cfg, ctrl.Options{
MetricsBindAddress: "0",
Scheme: scheme.Scheme,
})
Expect(err).ToNot(HaveOccurred())
testEventsH = controller.MakeEvents(k8sManager, "source-controller-test", nil)
err = (&GitRepositoryReconciler{
Client: k8sManager.GetClient(),
Events: testEventsH,
Storage: ginkgoTestStorage,
}).SetupWithManager(k8sManager)
Expect(err).ToNot(HaveOccurred(), "failed to setup GitRepositoryReconciler")
err = (&HelmRepositoryReconciler{
Client: k8sManager.GetClient(),
Scheme: scheme.Scheme,
Storage: ginkgoTestStorage,
Getters: getter.Providers{getter.Provider{
Schemes: []string{"http", "https"},
New: getter.NewHTTPGetter,
}},
}).SetupWithManager(k8sManager)
Expect(err).ToNot(HaveOccurred(), "failed to setup HelmRepositoryReconciler")
err = (&HelmChartReconciler{
Client: k8sManager.GetClient(),
Scheme: scheme.Scheme,
Storage: ginkgoTestStorage,
Getters: getter.Providers{getter.Provider{
Schemes: []string{"http", "https"},
New: getter.NewHTTPGetter,
}},
}).SetupWithManager(k8sManager)
Expect(err).ToNot(HaveOccurred(), "failed to setup HelmChartReconciler")
go func() {
err = k8sManager.Start(ctx)
Expect(err).ToNot(HaveOccurred())
}()
k8sClient = k8sManager.GetClient()
Expect(k8sClient).ToNot(BeNil())
close(done)
}, 60)
var _ = AfterSuite(func() {
By("tearing down the test environment")
if ginkgoTestStorage != nil {
err := os.RemoveAll(ginkgoTestStorage.BasePath)
Expect(err).NotTo(HaveOccurred())
}
err := ginkgoTestEnv.Stop()
Expect(err).ToNot(HaveOccurred())
})
func init() {
rand.Seed(time.Now().UnixNano())
}
func loadExampleKeys() (err error) {
examplePublicKey, err = os.ReadFile("testdata/certs/server.pem")
if err != nil {
return err
}
examplePrivateKey, err = os.ReadFile("testdata/certs/server-key.pem")
if err != nil {
return err
}
exampleCA, err = os.ReadFile("testdata/certs/ca.pem")
return err
}
var letterRunes = []rune("abcdefghijklmnopqrstuvwxyz1234567890")
func randStringRunes(n int) string {
b := make([]rune, n)
for i := range b {
b[i] = letterRunes[rand.Intn(len(letterRunes))]
}
return string(b)
}

View File

@ -29,6 +29,7 @@ import (
"strings"
"time"
securejoin "github.com/cyphar/filepath-securejoin"
"github.com/go-git/go-git/v5/plumbing/format/gitignore"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -52,7 +53,7 @@ type Storage struct {
Timeout time.Duration `json:"timeout"`
}
// NewStorage creates the storage helper for a given path and hostname
// NewStorage creates the storage helper for a given path and hostname.
func NewStorage(basePath string, hostname string, timeout time.Duration) (*Storage, error) {
if f, err := os.Stat(basePath); os.IsNotExist(err) || !f.IsDir() {
return nil, fmt.Errorf("invalid dir path: %s", basePath)
@ -80,11 +81,14 @@ func (s Storage) SetArtifactURL(artifact *sourcev1.Artifact) {
if artifact.Path == "" {
return
}
artifact.URL = fmt.Sprintf("http://%s/%s", s.Hostname, artifact.Path)
format := "http://%s/%s"
if strings.HasPrefix(s.Hostname, "http://") || strings.HasPrefix(s.Hostname, "https://") {
format = "%s/%s"
}
artifact.URL = fmt.Sprintf(format, s.Hostname, strings.TrimLeft(artifact.Path, "/"))
}
// SetHostname sets the hostname of the given URL string to the current Storage.Hostname
// and returns the result.
// SetHostname sets the hostname of the given URL string to the current Storage.Hostname and returns the result.
func (s Storage) SetHostname(URL string) string {
u, err := url.Parse(URL)
if err != nil {
@ -106,8 +110,7 @@ func (s *Storage) RemoveAll(artifact sourcev1.Artifact) error {
return os.RemoveAll(dir)
}
// RemoveAllButCurrent removes all files for the given v1beta1.Artifact base dir,
// excluding the current one.
// RemoveAllButCurrent removes all files for the given v1beta1.Artifact base dir, excluding the current one.
func (s *Storage) RemoveAllButCurrent(artifact sourcev1.Artifact) error {
localPath := s.LocalPath(artifact)
dir := filepath.Dir(localPath)
@ -132,8 +135,7 @@ func (s *Storage) RemoveAllButCurrent(artifact sourcev1.Artifact) error {
return nil
}
// ArtifactExist returns a boolean indicating whether the v1beta1.Artifact exists in storage
// and is a regular file.
// ArtifactExist returns a boolean indicating whether the v1beta1.Artifact exists in storage and is a regular file.
func (s *Storage) ArtifactExist(artifact sourcev1.Artifact) bool {
fi, err := os.Lstat(s.LocalPath(artifact))
if err != nil {
@ -142,14 +144,13 @@ func (s *Storage) ArtifactExist(artifact sourcev1.Artifact) bool {
return fi.Mode().IsRegular()
}
// ArchiveFileFilter must return true if a file should not be included
// in the archive after inspecting the given path and/or os.FileInfo.
// ArchiveFileFilter must return true if a file should not be included in the archive after inspecting the given path
// and/or os.FileInfo.
type ArchiveFileFilter func(p string, fi os.FileInfo) bool
// SourceIgnoreFilter returns an ArchiveFileFilter that filters out
// files matching sourceignore.VCSPatterns and any of the provided
// patterns. If an empty gitignore.Pattern slice is given, the matcher
// is set to sourceignore.NewDefaultMatcher.
// SourceIgnoreFilter returns an ArchiveFileFilter that filters out files matching sourceignore.VCSPatterns and any of
// the provided patterns.
// If an empty gitignore.Pattern slice is given, the matcher is set to sourceignore.NewDefaultMatcher.
func SourceIgnoreFilter(ps []gitignore.Pattern, domain []string) ArchiveFileFilter {
matcher := sourceignore.NewDefaultMatcher(ps, domain)
if len(ps) > 0 {
@ -163,10 +164,10 @@ func SourceIgnoreFilter(ps []gitignore.Pattern, domain []string) ArchiveFileFilt
}
}
// Archive atomically archives the given directory as a tarball to the
// given v1beta1.Artifact path, excluding directories and any
// ArchiveFileFilter matches. If successful, it sets the checksum and
// last update time on the artifact.
// Archive atomically archives the given directory as a tarball to the given v1beta1.Artifact path, excluding
// directories and any ArchiveFileFilter matches. While archiving, any environment specific data (for example,
// the user and group name) is stripped from file headers.
// If successful, it sets the checksum and last update time on the artifact.
func (s *Storage) Archive(artifact *sourcev1.Artifact, dir string, filter ArchiveFileFilter) (err error) {
if f, err := os.Stat(dir); os.IsNotExist(err) || !f.IsDir() {
return fmt.Errorf("invalid dir path: %s", dir)
@ -220,6 +221,16 @@ func (s *Storage) Archive(artifact *sourcev1.Artifact, dir string, filter Archiv
}
header.Name = relFilePath
// We want to remove any environment specific data as well, this
// ensures the checksum is purely content based.
header.Gid = 0
header.Uid = 0
header.Uname = ""
header.Gname = ""
header.ModTime = time.Time{}
header.AccessTime = time.Time{}
header.ChangeTime = time.Time{}
if err := tw.WriteHeader(header); err != nil {
return err
}
@ -341,9 +352,8 @@ func (s *Storage) Copy(artifact *sourcev1.Artifact, reader io.Reader) (err error
return nil
}
// CopyFromPath atomically copies the contents of the given path to the path of
// the v1beta1.Artifact. If successful, the checksum and last update time on the
// artifact is set.
// CopyFromPath atomically copies the contents of the given path to the path of the v1beta1.Artifact.
// If successful, the checksum and last update time on the artifact is set.
func (s *Storage) CopyFromPath(artifact *sourcev1.Artifact, path string) (err error) {
f, err := os.Open(path)
if err != nil {
@ -353,10 +363,10 @@ func (s *Storage) CopyFromPath(artifact *sourcev1.Artifact, path string) (err er
return s.Copy(artifact, f)
}
// CopyToPath copies the contents of the given artifact to the path.
// CopyToPath copies the contents in the (sub)path of the given artifact to the given path.
func (s *Storage) CopyToPath(artifact *sourcev1.Artifact, subPath, toPath string) error {
// create a tmp directory to store artifact
tmp, err := os.MkdirTemp("", "flux-include")
tmp, err := os.MkdirTemp("", "flux-include-")
if err != nil {
return err
}
@ -371,7 +381,7 @@ func (s *Storage) CopyToPath(artifact *sourcev1.Artifact, subPath, toPath string
defer f.Close()
// untar the artifact
untarPath := filepath.Join(tmp, "tar")
untarPath := filepath.Join(tmp, "unpack")
if _, err = untar.Untar(f, untarPath); err != nil {
return err
}
@ -382,15 +392,17 @@ func (s *Storage) CopyToPath(artifact *sourcev1.Artifact, subPath, toPath string
}
// copy the artifact content to the destination dir
fromPath := filepath.Join(untarPath, subPath)
fromPath, err := securejoin.SecureJoin(untarPath, subPath)
if err != nil {
return err
}
if err := fs.RenameWithFallback(fromPath, toPath); err != nil {
return err
}
return nil
}
// Symlink creates or updates a symbolic link for the given v1beta1.Artifact
// and returns the URL for the symlink.
// Symlink creates or updates a symbolic link for the given v1beta1.Artifact and returns the URL for the symlink.
func (s *Storage) Symlink(artifact sourcev1.Artifact, linkName string) (string, error) {
localPath := s.LocalPath(artifact)
dir := filepath.Dir(localPath)
@ -427,13 +439,16 @@ func (s *Storage) Lock(artifact sourcev1.Artifact) (unlock func(), err error) {
return mutex.Lock()
}
// LocalPath returns the local path of the given artifact (that is: relative to
// the Storage.BasePath).
// LocalPath returns the secure local path of the given artifact (that is: relative to the Storage.BasePath).
func (s *Storage) LocalPath(artifact sourcev1.Artifact) string {
if artifact.Path == "" {
return ""
}
return filepath.Join(s.BasePath, artifact.Path)
path, err := securejoin.SecureJoin(s.BasePath, artifact.Path)
if err != nil {
return ""
}
return path
}
// newHash returns a new SHA1 hash.

View File

@ -17,173 +17,131 @@ limitations under the License.
package controllers
import (
"fmt"
"math/rand"
"net/http"
"os"
"path/filepath"
"testing"
"time"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"helm.sh/helm/v3/pkg/getter"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/envtest"
"sigs.k8s.io/controller-runtime/pkg/envtest/printer"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
"github.com/fluxcd/pkg/runtime/controller"
"github.com/fluxcd/pkg/testserver"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
// +kubebuilder:scaffold:imports
"github.com/fluxcd/pkg/runtime/testenv"
)
// These tests use Ginkgo (BDD-style Go testing framework). Refer to
// http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
// These tests make use of plain Go using Gomega for assertions.
// At the beginning of every (sub)test Gomega can be initialized
// using gomega.NewWithT.
// Refer to http://onsi.github.io/gomega/ to learn more about
// Gomega.
var cfg *rest.Config
var k8sClient client.Client
var k8sManager ctrl.Manager
var testEnv *envtest.Environment
var storage *Storage
const (
timeout = 10 * time.Second
interval = 1 * time.Second
)
var examplePublicKey []byte
var examplePrivateKey []byte
var exampleCA []byte
var (
testEnv *testenv.Environment
testStorage *Storage
testServer *testserver.ArtifactServer
testEventsH controller.Events
testMetricsH controller.Metrics
ctx = ctrl.SetupSignalHandler()
)
func TestAPIs(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecsWithDefaultAndCustomReporters(t,
"Controller Suite",
[]Reporter{printer.NewlineReporter{}})
}
var _ = BeforeSuite(func(done Done) {
logf.SetLogger(
zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)),
)
By("bootstrapping test environment")
t := true
if os.Getenv("TEST_USE_EXISTING_CLUSTER") == "true" {
testEnv = &envtest.Environment{
UseExistingCluster: &t,
}
} else {
testEnv = &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")},
}
}
var err error
cfg, err = testEnv.Start()
Expect(err).ToNot(HaveOccurred())
Expect(cfg).ToNot(BeNil())
err = sourcev1.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
err = sourcev1.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
err = sourcev1.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
// +kubebuilder:scaffold:scheme
Expect(loadExampleKeys()).To(Succeed())
tmpStoragePath, err := os.MkdirTemp("", "source-controller-storage-")
Expect(err).NotTo(HaveOccurred(), "failed to create tmp storage dir")
storage, err = NewStorage(tmpStoragePath, "localhost:5050", time.Second*30)
Expect(err).NotTo(HaveOccurred(), "failed to create tmp storage")
// serve artifacts from the filesystem, as done in main.go
fs := http.FileServer(http.Dir(tmpStoragePath))
http.Handle("/", fs)
go http.ListenAndServe(":5050", nil)
k8sManager, err = ctrl.NewManager(cfg, ctrl.Options{
Scheme: scheme.Scheme,
})
Expect(err).ToNot(HaveOccurred())
err = (&GitRepositoryReconciler{
Client: k8sManager.GetClient(),
Scheme: scheme.Scheme,
Storage: storage,
}).SetupWithManager(k8sManager)
Expect(err).ToNot(HaveOccurred(), "failed to setup GtRepositoryReconciler")
err = (&HelmRepositoryReconciler{
Client: k8sManager.GetClient(),
Scheme: scheme.Scheme,
Storage: storage,
Getters: getter.Providers{getter.Provider{
Schemes: []string{"http", "https"},
New: getter.NewHTTPGetter,
}},
}).SetupWithManager(k8sManager)
Expect(err).ToNot(HaveOccurred(), "failed to setup HelmRepositoryReconciler")
err = (&HelmChartReconciler{
Client: k8sManager.GetClient(),
Scheme: scheme.Scheme,
Storage: storage,
Getters: getter.Providers{getter.Provider{
Schemes: []string{"http", "https"},
New: getter.NewHTTPGetter,
}},
}).SetupWithManager(k8sManager)
Expect(err).ToNot(HaveOccurred(), "failed to setup HelmChartReconciler")
go func() {
err = k8sManager.Start(ctrl.SetupSignalHandler())
Expect(err).ToNot(HaveOccurred())
}()
k8sClient = k8sManager.GetClient()
Expect(k8sClient).ToNot(BeNil())
close(done)
}, 60)
var _ = AfterSuite(func() {
By("tearing down the test environment")
if storage != nil {
err := os.RemoveAll(storage.BasePath)
Expect(err).NotTo(HaveOccurred())
}
err := testEnv.Stop()
Expect(err).ToNot(HaveOccurred())
})
var (
tlsPublicKey []byte
tlsPrivateKey []byte
tlsCA []byte
)
func init() {
rand.Seed(time.Now().UnixNano())
}
func loadExampleKeys() (err error) {
examplePublicKey, err = os.ReadFile("testdata/certs/server.pem")
func TestMain(m *testing.M) {
initTestTLS()
utilruntime.Must(sourcev1.AddToScheme(scheme.Scheme))
testEnv = testenv.New(testenv.WithCRDPath(filepath.Join("..", "config", "crd", "bases")))
var err error
testServer, err = testserver.NewTempArtifactServer()
if err != nil {
return err
panic(fmt.Sprintf("Failed to create a temporary storage server: %v", err))
}
examplePrivateKey, err = os.ReadFile("testdata/certs/server-key.pem")
fmt.Println("Starting the test storage server")
testServer.Start()
testStorage, err = newTestStorage(testServer.HTTPServer)
if err != nil {
return err
panic(fmt.Sprintf("Failed to create a test storage: %v", err))
}
exampleCA, err = os.ReadFile("testdata/certs/ca.pem")
return err
testEventsH = controller.MakeEvents(testEnv, "source-controller-test", nil)
testMetricsH = controller.MustMakeMetrics(testEnv)
if err := (&GitRepositoryReconciler{
Client: testEnv,
Events: testEventsH,
Metrics: testMetricsH,
Storage: testStorage,
}).SetupWithManager(testEnv); err != nil {
panic(fmt.Sprintf("Failed to start GitRepositoryReconciler: %v", err))
}
go func() {
fmt.Println("Starting the test environment")
if err := testEnv.Start(ctx); err != nil {
panic(fmt.Sprintf("Failed to start the test environment manager: %v", err))
}
}()
<-testEnv.Manager.Elected()
code := m.Run()
fmt.Println("Stopping the test environment")
if err := testEnv.Stop(); err != nil {
panic(fmt.Sprintf("Failed to stop the test environment: %v", err))
}
fmt.Println("Stopping the storage server")
testServer.Stop()
if err := os.RemoveAll(testServer.Root()); err != nil {
panic(fmt.Sprintf("Failed to remove storage server dir: %v", err))
}
os.Exit(code)
}
var letterRunes = []rune("abcdefghijklmnopqrstuvwxyz1234567890")
func randStringRunes(n int) string {
b := make([]rune, n)
for i := range b {
b[i] = letterRunes[rand.Intn(len(letterRunes))]
func initTestTLS() {
var err error
tlsPublicKey, err = os.ReadFile("testdata/certs/server.pem")
if err != nil {
panic(err)
}
tlsPrivateKey, err = os.ReadFile("testdata/certs/server-key.pem")
if err != nil {
panic(err)
}
tlsCA, err = os.ReadFile("testdata/certs/ca.pem")
if err != nil {
panic(err)
}
return string(b)
}
func newTestStorage(s *testserver.HTTPServer) (*Storage, error) {
storage, err := NewStorage(s.Root(), s.URL(), timeout)
if err != nil {
return nil, err
}
return storage, nil
}

View File

@ -0,0 +1 @@
**.txt

View File

View File

@ -0,0 +1,5 @@
---
apiVersion: v1
kind: Namespace
metadata:
name: dummy

View File

@ -299,10 +299,8 @@ github.com/fluxcd/pkg/apis/meta.LocalObjectReference
<td>
<em>(Optional)</em>
<p>The secret name containing the Git credentials.
For HTTPS repositories the secret must contain username and password
fields.
For SSH repositories the secret must contain identity, identity.pub and
known_hosts fields.</p>
For HTTPS repositories the secret must contain username and password fields.
For SSH repositories the secret must contain &lsquo;identity&rsquo;, &lsquo;identity.pub&rsquo; and &lsquo;known_hosts&rsquo; fields.</p>
</td>
</tr>
<tr>
@ -358,7 +356,7 @@ GitRepositoryVerification
</td>
<td>
<em>(Optional)</em>
<p>Verify OpenPGP signature for the Git commit HEAD points to.</p>
<p>Verification defines the configuration to verify the OpenPGP signature for the Git commit HEAD points to.</p>
</td>
</tr>
<tr>
@ -370,9 +368,8 @@ string
</td>
<td>
<em>(Optional)</em>
<p>Ignore overrides the set of excluded patterns in the .sourceignore format
(which is the same as .gitignore). If not provided, a default will be used,
consult the documentation for your version to find out what those are.</p>
<p>Ignore overrides the set of excluded patterns in the .sourceignore format (which is the same as .gitignore).
If not provided, a default will be used, consult the documentation for your version to find out what those are.</p>
</td>
</tr>
<tr>
@ -384,7 +381,8 @@ bool
</td>
<td>
<em>(Optional)</em>
<p>This flag tells the controller to suspend the reconciliation of this source.</p>
<p>Suspend tells the controller to suspend the reconciliation of this source.
This flag tells the controller to suspend the reconciliation of this source.</p>
</td>
</tr>
<tr>
@ -409,8 +407,7 @@ bool
</td>
<td>
<em>(Optional)</em>
<p>When enabled, after the clone is created, initializes all submodules within,
using their default settings.
<p>When enabled, after the clone is created, initializes all submodules within, using their default settings.
This option is available only when using the &lsquo;go-git&rsquo; GitImplementation.</p>
</td>
</tr>
@ -424,7 +421,8 @@ This option is available only when using the &lsquo;go-git&rsquo; GitImplementat
</em>
</td>
<td>
<p>Extra git repositories to map into the repository</p>
<p>Include defines a list of GitRepository resources which artifacts should be included in the artifact produced for
this resource.</p>
</td>
</tr>
</table>
@ -1264,10 +1262,8 @@ github.com/fluxcd/pkg/apis/meta.LocalObjectReference
<td>
<em>(Optional)</em>
<p>The secret name containing the Git credentials.
For HTTPS repositories the secret must contain username and password
fields.
For SSH repositories the secret must contain identity, identity.pub and
known_hosts fields.</p>
For HTTPS repositories the secret must contain username and password fields.
For SSH repositories the secret must contain &lsquo;identity&rsquo;, &lsquo;identity.pub&rsquo; and &lsquo;known_hosts&rsquo; fields.</p>
</td>
</tr>
<tr>
@ -1323,7 +1319,7 @@ GitRepositoryVerification
</td>
<td>
<em>(Optional)</em>
<p>Verify OpenPGP signature for the Git commit HEAD points to.</p>
<p>Verification defines the configuration to verify the OpenPGP signature for the Git commit HEAD points to.</p>
</td>
</tr>
<tr>
@ -1335,9 +1331,8 @@ string
</td>
<td>
<em>(Optional)</em>
<p>Ignore overrides the set of excluded patterns in the .sourceignore format
(which is the same as .gitignore). If not provided, a default will be used,
consult the documentation for your version to find out what those are.</p>
<p>Ignore overrides the set of excluded patterns in the .sourceignore format (which is the same as .gitignore).
If not provided, a default will be used, consult the documentation for your version to find out what those are.</p>
</td>
</tr>
<tr>
@ -1349,7 +1344,8 @@ bool
</td>
<td>
<em>(Optional)</em>
<p>This flag tells the controller to suspend the reconciliation of this source.</p>
<p>Suspend tells the controller to suspend the reconciliation of this source.
This flag tells the controller to suspend the reconciliation of this source.</p>
</td>
</tr>
<tr>
@ -1374,8 +1370,7 @@ bool
</td>
<td>
<em>(Optional)</em>
<p>When enabled, after the clone is created, initializes all submodules within,
using their default settings.
<p>When enabled, after the clone is created, initializes all submodules within, using their default settings.
This option is available only when using the &lsquo;go-git&rsquo; GitImplementation.</p>
</td>
</tr>
@ -1389,7 +1384,8 @@ This option is available only when using the &lsquo;go-git&rsquo; GitImplementat
</em>
</td>
<td>
<p>Extra git repositories to map into the repository</p>
<p>Include defines a list of GitRepository resources which artifacts should be included in the artifact produced for
this resource.</p>
</td>
</tr>
</tbody>
@ -1448,8 +1444,7 @@ string
</td>
<td>
<em>(Optional)</em>
<p>URL is the download link for the artifact output of the last repository
sync.</p>
<p>URL is the download link for the artifact output of the last repository sync.</p>
</td>
</tr>
<tr>
@ -1524,7 +1519,7 @@ string
</em>
</td>
<td>
<p>Mode describes what git object should be verified, currently (&lsquo;head&rsquo;).</p>
<p>Mode describes what Git object should be verified, currently (&lsquo;head&rsquo;).</p>
</td>
</tr>
<tr>
@ -1537,7 +1532,7 @@ github.com/fluxcd/pkg/apis/meta.LocalObjectReference
</em>
</td>
<td>
<p>The secret name containing the public keys of all trusted Git authors.</p>
<p>SecretRef containing the public keys of all trusted Git authors.</p>
</td>
</tr>
</tbody>

19
go.mod
View File

@ -6,15 +6,15 @@ replace github.com/fluxcd/source-controller/api => ./api
require (
github.com/Masterminds/semver/v3 v3.1.1
github.com/blang/semver/v4 v4.0.0
github.com/cyphar/filepath-securejoin v0.2.2
github.com/fluxcd/pkg/apis/meta v0.10.0
github.com/fluxcd/pkg/gittestserver v0.3.0
github.com/fluxcd/pkg/apis/meta v0.11.0-rc.1
github.com/fluxcd/pkg/gittestserver v0.3.2
github.com/fluxcd/pkg/gitutil v0.1.0
github.com/fluxcd/pkg/helmtestserver v0.2.0
github.com/fluxcd/pkg/lockedfile v0.1.0
github.com/fluxcd/pkg/runtime v0.12.0
github.com/fluxcd/pkg/runtime v0.13.0-rc.2
github.com/fluxcd/pkg/ssh v0.1.0
github.com/fluxcd/pkg/testserver v0.1.0
github.com/fluxcd/pkg/untar v0.1.0
github.com/fluxcd/pkg/version v0.1.0
github.com/fluxcd/source-controller/api v0.15.3
@ -27,13 +27,14 @@ require (
github.com/onsi/gomega v1.13.0
github.com/spf13/pflag v1.0.5
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
gotest.tools v2.2.0+incompatible
helm.sh/helm/v3 v3.6.3
k8s.io/api v0.21.1
k8s.io/apimachinery v0.21.1
k8s.io/client-go v0.21.1
sigs.k8s.io/controller-runtime v0.9.0
k8s.io/api v0.21.2
k8s.io/apimachinery v0.21.3
k8s.io/client-go v0.21.2
k8s.io/utils v0.0.0-20210527160623-6fdb442a123b
sigs.k8s.io/controller-runtime v0.9.3
sigs.k8s.io/yaml v1.2.0
)

72
go.sum
View File

@ -111,10 +111,7 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/bshuster-repo/logrus-logstash-hook v0.4.1 h1:pgAtgj+A31JBVtEHu2uHuEx0n+2ukqUJnS2vVe5pQNA=
github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
@ -229,18 +226,18 @@ github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZM
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fluxcd/pkg/apis/meta v0.10.0 h1:N7wVGHC1cyPdT87hrDC7UwCwRwnZdQM46PBSLjG2rlE=
github.com/fluxcd/pkg/apis/meta v0.10.0/go.mod h1:CW9X9ijMTpNe7BwnokiUOrLl/h13miwVr/3abEQLbKE=
github.com/fluxcd/pkg/gittestserver v0.3.0 h1:6aa30mybecBwBWaJ2IEk7pQzefWnjWjxkTSrHMHawvg=
github.com/fluxcd/pkg/gittestserver v0.3.0/go.mod h1:8j36Z6B0BuKNZZ6exAWoyDEpyQoFcjz1IX3WBT7PZNg=
github.com/fluxcd/pkg/apis/meta v0.11.0-rc.1 h1:RHHrztAFv9wmjM+Pk7Svt1UdD+1SdnQSp76MWFiM7Hg=
github.com/fluxcd/pkg/apis/meta v0.11.0-rc.1/go.mod h1:yUblM2vg+X8TE3A2VvJfdhkGmg+uqBlSPkLk7dxi0UM=
github.com/fluxcd/pkg/gittestserver v0.3.2 h1:oc1OoZ4b+kAu0vu/RT9wUwuQZxSqEjBOlQWYYA+YeLM=
github.com/fluxcd/pkg/gittestserver v0.3.2/go.mod h1:8j36Z6B0BuKNZZ6exAWoyDEpyQoFcjz1IX3WBT7PZNg=
github.com/fluxcd/pkg/gitutil v0.1.0 h1:VO3kJY/CKOCO4ysDNqfdpTg04icAKBOSb3lbR5uE/IE=
github.com/fluxcd/pkg/gitutil v0.1.0/go.mod h1:Ybz50Ck5gkcnvF0TagaMwtlRy3X3wXuiri1HVsK5id4=
github.com/fluxcd/pkg/helmtestserver v0.2.0 h1:cE7YHDmrWI0hr9QpaaeQ0vQ16Z0IiqZKiINDpqdY610=
github.com/fluxcd/pkg/helmtestserver v0.2.0/go.mod h1:Yie8n7xuu5Nvf1Q7302LKsubJhWpwzCaK0rLJvmF7aI=
github.com/fluxcd/pkg/lockedfile v0.1.0 h1:YsYFAkd6wawMCcD74ikadAKXA4s2sukdxrn7w8RB5eo=
github.com/fluxcd/pkg/lockedfile v0.1.0/go.mod h1:EJLan8t9MiOcgTs8+puDjbE6I/KAfHbdvIy9VUgIjm8=
github.com/fluxcd/pkg/runtime v0.12.0 h1:BPZZ8bBkimpqGAPXqOf3LTaw+tcw6HgbWyCuzbbsJGs=
github.com/fluxcd/pkg/runtime v0.12.0/go.mod h1:EyaTR2TOYcjL5U//C4yH3bt2tvTgIOSXpVRbWxUn/C4=
github.com/fluxcd/pkg/runtime v0.13.0-rc.2 h1:+4uTEg+CU++hlr7NpOP4KYp60MtHDOgYvpz/74tbATg=
github.com/fluxcd/pkg/runtime v0.13.0-rc.2/go.mod h1:TmvE2cJl1QkgZNmmlr7XUKoWDQwUiM5/wTUxXsQVoc8=
github.com/fluxcd/pkg/ssh v0.1.0 h1:cym2bqiT4IINOdLV0J6GYxer16Ii/7b2+RlK3CG+CnA=
github.com/fluxcd/pkg/ssh v0.1.0/go.mod h1:KUuVhaB6AX3IHTGCd3Ti/nesn5t1Nz4zCThFkkjHctM=
github.com/fluxcd/pkg/testserver v0.1.0 h1:nOYgM1HYFZNNSUFykuWDmrsxj4jQxUCvmLHWOQeqmyA=
@ -841,6 +838,7 @@ github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6Ut
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI=
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE=
@ -939,6 +937,7 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -979,8 +978,10 @@ golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -994,8 +995,9 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -1052,10 +1054,14 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 h1:JWgyZ1qgdTaF3N3oxC+MdTV7qvEEgHo3otj+HB5CM7Q=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE=
@ -1072,8 +1078,9 @@ golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE=
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6 h1:Vv0JUPWTyeqUq42B2WJ1FeIDjjvGKoA2Ss+Ts0lAVbs=
golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -1122,8 +1129,9 @@ golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roY
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.4 h1:cVngSRcfgyZCzys3KYOpCFa+4dqX/Oub9tAq00ttGVs=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -1258,27 +1266,28 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
k8s.io/api v0.21.0/go.mod h1:+YbrhBBGgsxbF6o6Kj4KJPJnBmAKuXDeS3E18bgHNVU=
k8s.io/api v0.21.1 h1:94bbZ5NTjdINJEdzOkpS4vdPhkb1VFpTYC9zh43f75c=
k8s.io/api v0.21.1/go.mod h1:FstGROTmsSHBarKc8bylzXih8BLNYTiS3TZcsoEDg2s=
k8s.io/api v0.21.2 h1:vz7DqmRsXTCSa6pNxXwQ1IYeAZgdIsua+DZU+o+SX3Y=
k8s.io/api v0.21.2/go.mod h1:Lv6UGJZ1rlMI1qusN8ruAp9PUBFyBwpEHAdG24vIsiU=
k8s.io/apiextensions-apiserver v0.21.0/go.mod h1:gsQGNtGkc/YoDG9loKI0V+oLZM4ljRPjc/sql5tmvzc=
k8s.io/apiextensions-apiserver v0.21.1 h1:AA+cnsb6w7SZ1vD32Z+zdgfXdXY8X9uGX5bN6EoPEIo=
k8s.io/apiextensions-apiserver v0.21.1/go.mod h1:KESQFCGjqVcVsZ9g0xX5bacMjyX5emuWcS2arzdEouA=
k8s.io/apiextensions-apiserver v0.21.2 h1:+exKMRep4pDrphEafRvpEi79wTnCFMqKf8LBtlA3yrE=
k8s.io/apiextensions-apiserver v0.21.2/go.mod h1:+Axoz5/l3AYpGLlhJDfcVQzCerVYq3K3CvDMvw6X1RA=
k8s.io/apimachinery v0.21.0/go.mod h1:jbreFvJo3ov9rj7eWT7+sYiRx+qZuCYXwWT1bcDswPY=
k8s.io/apimachinery v0.21.1 h1:Q6XuHGlj2xc+hlMCvqyYfbv3H7SRGn2c8NycxJquDVs=
k8s.io/apimachinery v0.21.1/go.mod h1:jbreFvJo3ov9rj7eWT7+sYiRx+qZuCYXwWT1bcDswPY=
k8s.io/apimachinery v0.21.2/go.mod h1:CdTY8fU/BlvAbJ2z/8kBwimGki5Zp8/fbVuLY8gJumM=
k8s.io/apimachinery v0.21.3 h1:3Ju4nvjCngxxMYby0BimUk+pQHPOQp3eCGChk5kfVII=
k8s.io/apimachinery v0.21.3/go.mod h1:H/IM+5vH9kZRNJ4l3x/fXP/5bOPJaVP/guptnZPeCFI=
k8s.io/apiserver v0.21.0/go.mod h1:w2YSn4/WIwYuxG5zJmcqtRdtqgW/J2JRgFAqps3bBpg=
k8s.io/apiserver v0.21.1 h1:wTRcid53IhxhbFt4KTrFSw8tAncfr01EP91lzfcygVg=
k8s.io/apiserver v0.21.1/go.mod h1:nLLYZvMWn35glJ4/FZRhzLG/3MPxAaZTgV4FJZdr+tY=
k8s.io/apiserver v0.21.2 h1:vfGLD8biFXHzbcIEXyW3652lDwkV8tZEFJAaS2iuJlw=
k8s.io/apiserver v0.21.2/go.mod h1:lN4yBoGyiNT7SC1dmNk0ue6a5Wi6O3SWOIw91TsucQw=
k8s.io/cli-runtime v0.21.0 h1:/V2Kkxtf6x5NI2z+Sd/mIrq4FQyQ8jzZAUD6N5RnN7Y=
k8s.io/cli-runtime v0.21.0/go.mod h1:XoaHP93mGPF37MkLbjGVYqg3S1MnsFdKtiA/RZzzxOo=
k8s.io/client-go v0.21.0/go.mod h1:nNBytTF9qPFDEhoqgEPaarobC8QPae13bElIVHzIglA=
k8s.io/client-go v0.21.1 h1:bhblWYLZKUu+pm50plvQF8WpY6TXdRRtcS/K9WauOj4=
k8s.io/client-go v0.21.1/go.mod h1:/kEw4RgW+3xnBGzvp9IWxKSNA+lXn3A7AuH3gdOAzLs=
k8s.io/client-go v0.21.2 h1:Q1j4L/iMN4pTw6Y4DWppBoUxgKO8LbffEMVEV00MUp0=
k8s.io/client-go v0.21.2/go.mod h1:HdJ9iknWpbl3vMGtib6T2PyI/VYxiZfq936WNVHBRrA=
k8s.io/code-generator v0.21.0/go.mod h1:hUlps5+9QaTrKx+jiM4rmq7YmH8wPOIko64uZCHDh6Q=
k8s.io/code-generator v0.21.1/go.mod h1:hUlps5+9QaTrKx+jiM4rmq7YmH8wPOIko64uZCHDh6Q=
k8s.io/code-generator v0.21.2/go.mod h1:8mXJDCB7HcRo1xiEQstcguZkbxZaqeUOrO9SsicWs3U=
k8s.io/component-base v0.21.0/go.mod h1:qvtjz6X0USWXbgmbfXR+Agik4RZ3jv2Bgr5QnZzdPYw=
k8s.io/component-base v0.21.1 h1:iLpj2btXbR326s/xNQWmPNGu0gaYSjzn7IN/5i28nQw=
k8s.io/component-base v0.21.1/go.mod h1:NgzFZ2qu4m1juby4TnrmpR8adRk6ka62YdH5DkIIyKA=
k8s.io/component-base v0.21.2 h1:EsnmFFoJ86cEywC0DoIkAUiEV6fjgauNugiw1lmIjs4=
k8s.io/component-base v0.21.2/go.mod h1:9lvmIThzdlrJj5Hp8Z/TOgIkdfsNARQ1pT+3PByuiuc=
k8s.io/component-helpers v0.21.0/go.mod h1:tezqefP7lxfvJyR+0a+6QtVrkZ/wIkyMLK4WcQ3Cj8U=
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
@ -1299,8 +1308,10 @@ rsc.io/letsencrypt v0.0.3/go.mod h1:buyQKZ6IXrRnB7TdkHP0RyEybLx18HHyOSoTyoOLqNY=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
sigs.k8s.io/controller-runtime v0.9.0 h1:ZIZ/dtpboPSbZYY7uUz2OzrkaBTOThx2yekLtpGB+zY=
sigs.k8s.io/controller-runtime v0.9.0/go.mod h1:TgkfvrhhEw3PlI0BRL/5xM+89y3/yc0ZDfdbTl84si8=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.19/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
sigs.k8s.io/controller-runtime v0.9.2/go.mod h1:TxzMCHyEUpaeuOiZx/bIdc2T81vfs/aKdvJt9wuu0zk=
sigs.k8s.io/controller-runtime v0.9.3 h1:n075bHQ1wb8hpX7C27pNrqsb0fj8mcfCQfNX+oKTbYE=
sigs.k8s.io/controller-runtime v0.9.3/go.mod h1:TxzMCHyEUpaeuOiZx/bIdc2T81vfs/aKdvJt9wuu0zk=
sigs.k8s.io/kustomize/api v0.8.5 h1:bfCXGXDAbFbb/Jv5AhMj2BB8a5VAJuuQ5/KU69WtDjQ=
sigs.k8s.io/kustomize/api v0.8.5/go.mod h1:M377apnKT5ZHJS++6H4rQoCHmWtt6qTpp3mbe7p6OLY=
sigs.k8s.io/kustomize/cmd/config v0.9.7/go.mod h1:MvXCpHs77cfyxRmCNUQjIqCmZyYsbn5PyQpWiq44nW0=
@ -1308,8 +1319,9 @@ sigs.k8s.io/kustomize/kustomize/v4 v4.0.5/go.mod h1:C7rYla7sI8EnxHE/xEhRBSHMNfcL
sigs.k8s.io/kustomize/kyaml v0.10.15 h1:dSLgG78KyaxN4HylPXdK+7zB3k7sW6q3IcCmcfKA+aI=
sigs.k8s.io/kustomize/kyaml v0.10.15/go.mod h1:mlQFagmkm1P+W4lZJbJ/yaxMd8PqMRSC4cPcfUVt5Hg=
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/structured-merge-diff/v4 v4.1.0 h1:C4r9BgJ98vrKnnVCjwCSXcWjWe0NKcUQkmzDXZXGwH8=
sigs.k8s.io/structured-merge-diff/v4 v4.1.0/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno=
sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=

46
main.go
View File

@ -25,6 +25,13 @@ import (
"strings"
"time"
"github.com/fluxcd/pkg/runtime/client"
helper "github.com/fluxcd/pkg/runtime/controller"
"github.com/fluxcd/pkg/runtime/events"
"github.com/fluxcd/pkg/runtime/leaderelection"
"github.com/fluxcd/pkg/runtime/logger"
"github.com/fluxcd/pkg/runtime/pprof"
"github.com/fluxcd/pkg/runtime/probes"
"github.com/go-logr/logr"
flag "github.com/spf13/pflag"
"helm.sh/helm/v3/pkg/getter"
@ -33,15 +40,6 @@ import (
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
ctrl "sigs.k8s.io/controller-runtime"
crtlmetrics "sigs.k8s.io/controller-runtime/pkg/metrics"
"github.com/fluxcd/pkg/runtime/client"
"github.com/fluxcd/pkg/runtime/events"
"github.com/fluxcd/pkg/runtime/leaderelection"
"github.com/fluxcd/pkg/runtime/logger"
"github.com/fluxcd/pkg/runtime/metrics"
"github.com/fluxcd/pkg/runtime/pprof"
"github.com/fluxcd/pkg/runtime/probes"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/fluxcd/source-controller/controllers"
@ -99,26 +97,25 @@ func main() {
flag.BoolVar(&watchAllNamespaces, "watch-all-namespaces", true,
"Watch for custom resources in all namespaces, if set to false it will only watch the runtime namespace.")
flag.DurationVar(&requeueDependency, "requeue-dependency", 30*time.Second, "The interval at which failing dependencies are reevaluated.")
clientOptions.BindFlags(flag.CommandLine)
logOptions.BindFlags(flag.CommandLine)
leaderElectionOptions.BindFlags(flag.CommandLine)
flag.Parse()
ctrl.SetLogger(logger.NewLogger(logOptions))
var eventRecorder *events.Recorder
if eventsAddr != "" {
if er, err := events.NewRecorder(eventsAddr, controllerName); err != nil {
er, err := events.NewRecorder(eventsAddr, controllerName)
if err != nil {
setupLog.Error(err, "unable to create event recorder")
os.Exit(1)
} else {
eventRecorder = er
}
eventRecorder = er
}
metricsRecorder := metrics.NewRecorder()
crtlmetrics.Registry.MustRegister(metricsRecorder.Collectors()...)
watchNamespace := ""
if !watchAllNamespaces {
watchNamespace = os.Getenv("RUNTIME_NAMESPACE")
@ -147,18 +144,19 @@ func main() {
probes.SetupChecks(mgr, setupLog)
pprof.SetupHandlers(mgr, setupLog)
eventsH := helper.MakeEvents(mgr, controllerName, eventRecorder)
metricsH := helper.MustMakeMetrics(mgr)
if storageAdvAddr == "" {
storageAdvAddr = determineAdvStorageAddr(storageAddr, setupLog)
}
storage := mustInitStorage(storagePath, storageAdvAddr, setupLog)
if err = (&controllers.GitRepositoryReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Storage: storage,
EventRecorder: mgr.GetEventRecorderFor(controllerName),
ExternalEventRecorder: eventRecorder,
MetricsRecorder: metricsRecorder,
Client: mgr.GetClient(),
Events: eventsH,
Metrics: metricsH,
Storage: storage,
}).SetupWithManagerAndOptions(mgr, controllers.GitRepositoryReconcilerOptions{
MaxConcurrentReconciles: concurrent,
DependencyRequeueInterval: requeueDependency,
@ -173,7 +171,7 @@ func main() {
Getters: getters,
EventRecorder: mgr.GetEventRecorderFor(controllerName),
ExternalEventRecorder: eventRecorder,
MetricsRecorder: metricsRecorder,
MetricsRecorder: metricsH.MetricsRecorder,
}).SetupWithManagerAndOptions(mgr, controllers.HelmRepositoryReconcilerOptions{
MaxConcurrentReconciles: concurrent,
}); err != nil {
@ -187,7 +185,7 @@ func main() {
Getters: getters,
EventRecorder: mgr.GetEventRecorderFor(controllerName),
ExternalEventRecorder: eventRecorder,
MetricsRecorder: metricsRecorder,
MetricsRecorder: metricsH.MetricsRecorder,
}).SetupWithManagerAndOptions(mgr, controllers.HelmChartReconcilerOptions{
MaxConcurrentReconciles: concurrent,
}); err != nil {
@ -200,7 +198,7 @@ func main() {
Storage: storage,
EventRecorder: mgr.GetEventRecorderFor(controllerName),
ExternalEventRecorder: eventRecorder,
MetricsRecorder: metricsRecorder,
MetricsRecorder: metricsH.MetricsRecorder,
}).SetupWithManagerAndOptions(mgr, controllers.BucketReconcilerOptions{
MaxConcurrentReconciles: concurrent,
}); err != nil {

46
pkg/git/fake/commit.go Normal file
View File

@ -0,0 +1,46 @@
/*
Copyright 2021 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package fake
import (
"errors"
corev1 "k8s.io/api/core/v1"
)
type Commit struct {
valid bool
hash string
}
func NewCommit(valid bool, hash string) Commit {
return Commit{
valid: valid,
hash: hash,
}
}
func (c Commit) Verify(secret corev1.Secret) error {
if !c.valid {
return errors.New("invalid signature")
}
return nil
}
func (c Commit) Hash() string {
return c.hash
}

View File

@ -193,7 +193,7 @@ func (c *CheckoutSemVer) Checkout(ctx context.Context, path, url string, auth *g
tags := make(map[string]string)
tagTimestamps := make(map[string]time.Time)
_ = repoTags.ForEach(func(t *plumbing.Reference) error {
if err = repoTags.ForEach(func(t *plumbing.Reference) error {
revision := plumbing.Revision(t.Name().String())
hash, err := repo.ResolveRevision(revision)
if err != nil {
@ -207,7 +207,9 @@ func (c *CheckoutSemVer) Checkout(ctx context.Context, path, url string, auth *g
tags[t.Name().Short()] = t.Strings()[1]
return nil
})
}); err != nil {
return nil, "", err
}
var matchedVersions semver.Collection
for tag, _ := range tags {

View File

@ -19,8 +19,11 @@ package libgit2
import (
"context"
"fmt"
"sort"
"time"
"github.com/blang/semver/v4"
"github.com/Masterminds/semver/v3"
"github.com/fluxcd/pkg/version"
git2go "github.com/libgit2/git2go/v31"
"github.com/fluxcd/pkg/gitutil"
@ -168,7 +171,7 @@ type CheckoutSemVer struct {
}
func (c *CheckoutSemVer) Checkout(ctx context.Context, path, url string, auth *git.Auth) (git.Commit, string, error) {
rng, err := semver.ParseRange(c.semVer)
verConstraint, err := semver.NewConstraint(c.semVer)
if err != nil {
return nil, "", fmt.Errorf("semver parse range error: %w", err)
}
@ -186,28 +189,61 @@ func (c *CheckoutSemVer) Checkout(ctx context.Context, path, url string, auth *g
return nil, "", fmt.Errorf("unable to clone '%s', error: %w", url, err)
}
repoTags, err := repo.Tags.List()
if err != nil {
return nil, "", fmt.Errorf("git list tags error: %w", err)
}
svTags := make(map[string]string)
var svers []semver.Version
for _, tag := range repoTags {
v, _ := semver.ParseTolerant(tag)
if rng(v) {
svers = append(svers, v)
svTags[v.String()] = tag
tags := make(map[string]string)
tagTimestamps := make(map[string]time.Time)
if err := repo.Tags.Foreach(func(name string, id *git2go.Oid) error {
tag, err := repo.LookupTag(id)
if err != nil {
return nil
}
commit, err := tag.Peel(git2go.ObjectCommit)
if err != nil {
return fmt.Errorf("can't get commit for tag %s: %w", name, err)
}
c, err := commit.AsCommit()
if err != nil {
return err
}
tagTimestamps[tag.Name()] = c.Committer().When
tags[tag.Name()] = name
return nil
}); err != nil {
return nil, "", err
}
if len(svers) == 0 {
var matchedVersions semver.Collection
for tag, _ := range tags {
v, err := version.ParseVersion(tag)
if err != nil {
continue
}
if !verConstraint.Check(v) {
continue
}
matchedVersions = append(matchedVersions, v)
}
if len(matchedVersions) == 0 {
return nil, "", fmt.Errorf("no match found for semver: %s", c.semVer)
}
semver.Sort(svers)
v := svers[len(svers)-1]
t := svTags[v.String()]
// Sort versions
sort.SliceStable(matchedVersions, func(i, j int) bool {
left := matchedVersions[i]
right := matchedVersions[j]
if !left.Equal(right) {
return left.LessThan(right)
}
// Having tag target timestamps at our disposal, we further try to sort
// versions into a chronological order. This is especially important for
// versions that differ only by build metadata, because it is not considered
// a part of the comparable version in Semver
return tagTimestamps[left.String()].Before(tagTimestamps[right.String()])
})
v := matchedVersions[len(matchedVersions)-1]
t := v.Original()
ref, err := repo.References.Dwim(t)
if err != nil {