@@ -17,14 +17,14 @@ limitations under the License.
17
17
package controllers
18
18
19
19
import (
20
- "bytes"
21
20
"context"
22
21
"fmt"
23
22
"net/url"
23
+ "os"
24
24
"time"
25
25
26
26
"github.com/go-logr/logr"
27
- "helm.sh/helm/v3/pkg/getter"
27
+ helmgetter "helm.sh/helm/v3/pkg/getter"
28
28
corev1 "k8s.io/api/core/v1"
29
29
apimeta "k8s.io/apimachinery/pkg/api/meta"
30
30
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -37,15 +37,15 @@ import (
37
37
"sigs.k8s.io/controller-runtime/pkg/controller"
38
38
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
39
39
"sigs.k8s.io/controller-runtime/pkg/predicate"
40
- "sigs.k8s.io/yaml"
41
40
42
41
"github.com/fluxcd/pkg/apis/meta"
43
42
"github.com/fluxcd/pkg/runtime/events"
44
43
"github.com/fluxcd/pkg/runtime/metrics"
45
44
"github.com/fluxcd/pkg/runtime/predicates"
46
45
47
46
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
48
- "github.com/fluxcd/source-controller/internal/helm"
47
+ "github.com/fluxcd/source-controller/internal/helm/getter"
48
+ "github.com/fluxcd/source-controller/internal/helm/repository"
49
49
)
50
50
51
51
// +kubebuilder:rbac:groups=source.toolkit.fluxcd.io,resources=helmrepositories,verbs=get;list;watch;create;update;patch;delete
@@ -58,7 +58,7 @@ type HelmRepositoryReconciler struct {
58
58
client.Client
59
59
Scheme * runtime.Scheme
60
60
Storage * Storage
61
- Getters getter .Providers
61
+ Getters helmgetter .Providers
62
62
EventRecorder kuberecorder.EventRecorder
63
63
ExternalEventRecorder * events.Recorder
64
64
MetricsRecorder * metrics.Recorder
@@ -170,96 +170,108 @@ func (r *HelmRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Reque
170
170
return ctrl.Result {RequeueAfter : repository .GetInterval ().Duration }, nil
171
171
}
172
172
173
- func (r * HelmRepositoryReconciler ) reconcile (ctx context.Context , repository sourcev1.HelmRepository ) (sourcev1.HelmRepository , error ) {
174
- clientOpts := []getter .Option {
175
- getter .WithURL (repository .Spec .URL ),
176
- getter .WithTimeout (repository .Spec .Timeout .Duration ),
177
- getter .WithPassCredentialsAll (repository .Spec .PassCredentials ),
173
+ func (r * HelmRepositoryReconciler ) reconcile (ctx context.Context , repo sourcev1.HelmRepository ) (sourcev1.HelmRepository , error ) {
174
+ clientOpts := []helmgetter .Option {
175
+ helmgetter .WithURL (repo .Spec .URL ),
176
+ helmgetter .WithTimeout (repo .Spec .Timeout .Duration ),
177
+ helmgetter .WithPassCredentialsAll (repo .Spec .PassCredentials ),
178
178
}
179
- if repository .Spec .SecretRef != nil {
179
+ if repo .Spec .SecretRef != nil {
180
180
name := types.NamespacedName {
181
- Namespace : repository .GetNamespace (),
182
- Name : repository .Spec .SecretRef .Name ,
181
+ Namespace : repo .GetNamespace (),
182
+ Name : repo .Spec .SecretRef .Name ,
183
183
}
184
184
185
185
var secret corev1.Secret
186
186
err := r .Client .Get (ctx , name , & secret )
187
187
if err != nil {
188
188
err = fmt .Errorf ("auth secret error: %w" , err )
189
- return sourcev1 .HelmRepositoryNotReady (repository , sourcev1 .AuthenticationFailedReason , err .Error ()), err
189
+ return sourcev1 .HelmRepositoryNotReady (repo , sourcev1 .AuthenticationFailedReason , err .Error ()), err
190
190
}
191
191
192
- opts , cleanup , err := helm .ClientOptionsFromSecret (secret )
192
+ authDir , err := os .MkdirTemp ("" , repo .Kind + "-" + repo .Namespace + "-" + repo .Name + "-" )
193
+ if err != nil {
194
+ err = fmt .Errorf ("failed to create temporary working directory for credentials: %w" , err )
195
+ return sourcev1 .HelmRepositoryNotReady (repo , sourcev1 .AuthenticationFailedReason , err .Error ()), err
196
+ }
197
+ defer os .RemoveAll (authDir )
198
+
199
+ opts , err := getter .ClientOptionsFromSecret (authDir , secret )
193
200
if err != nil {
194
201
err = fmt .Errorf ("auth options error: %w" , err )
195
- return sourcev1 .HelmRepositoryNotReady (repository , sourcev1 .AuthenticationFailedReason , err .Error ()), err
202
+ return sourcev1 .HelmRepositoryNotReady (repo , sourcev1 .AuthenticationFailedReason , err .Error ()), err
196
203
}
197
- defer cleanup ()
198
204
clientOpts = append (clientOpts , opts ... )
199
205
}
200
206
201
- chartRepo , err := helm .NewChartRepository (repository .Spec .URL , r .Getters , clientOpts )
207
+ chartRepo , err := repository .NewChartRepository (repo .Spec .URL , "" , r .Getters , clientOpts )
202
208
if err != nil {
203
209
switch err .(type ) {
204
210
case * url.Error :
205
- return sourcev1 .HelmRepositoryNotReady (repository , sourcev1 .URLInvalidReason , err .Error ()), err
211
+ return sourcev1 .HelmRepositoryNotReady (repo , sourcev1 .URLInvalidReason , err .Error ()), err
206
212
default :
207
- return sourcev1 .HelmRepositoryNotReady (repository , sourcev1 .IndexationFailedReason , err .Error ()), err
213
+ return sourcev1 .HelmRepositoryNotReady (repo , sourcev1 .IndexationFailedReason , err .Error ()), err
208
214
}
209
215
}
210
- if err := chartRepo .DownloadIndex (); err != nil {
216
+ checksum , err := chartRepo .CacheIndex ()
217
+ if err != nil {
211
218
err = fmt .Errorf ("failed to download repository index: %w" , err )
212
- return sourcev1 .HelmRepositoryNotReady (repository , sourcev1 .IndexationFailedReason , err .Error ()), err
219
+ return sourcev1 .HelmRepositoryNotReady (repo , sourcev1 .IndexationFailedReason , err .Error ()), err
213
220
}
221
+ defer chartRepo .RemoveCache ()
214
222
215
- indexBytes , err := yaml .Marshal (& chartRepo .Index )
216
- if err != nil {
217
- return sourcev1 .HelmRepositoryNotReady (repository , sourcev1 .StorageOperationFailedReason , err .Error ()), err
218
- }
219
- hash := r .Storage .Checksum (bytes .NewReader (indexBytes ))
220
- artifact := r .Storage .NewArtifactFor (repository .Kind ,
221
- repository .ObjectMeta .GetObjectMeta (),
222
- hash ,
223
- fmt .Sprintf ("index-%s.yaml" , hash ))
224
- // return early on unchanged index
225
- if apimeta .IsStatusConditionTrue (repository .Status .Conditions , meta .ReadyCondition ) && repository .GetArtifact ().HasRevision (artifact .Revision ) {
226
- if artifact .URL != repository .GetArtifact ().URL {
227
- r .Storage .SetArtifactURL (repository .GetArtifact ())
228
- repository .Status .URL = r .Storage .SetHostname (repository .Status .URL )
223
+ artifact := r .Storage .NewArtifactFor (repo .Kind ,
224
+ repo .ObjectMeta .GetObjectMeta (),
225
+ "" ,
226
+ fmt .Sprintf ("index-%s.yaml" , checksum ))
227
+
228
+ // Return early on unchanged index
229
+ if apimeta .IsStatusConditionTrue (repo .Status .Conditions , meta .ReadyCondition ) &&
230
+ (repo .GetArtifact () != nil && repo .GetArtifact ().Checksum == checksum ) {
231
+ if artifact .URL != repo .GetArtifact ().URL {
232
+ r .Storage .SetArtifactURL (repo .GetArtifact ())
233
+ repo .Status .URL = r .Storage .SetHostname (repo .Status .URL )
229
234
}
230
- return repository , nil
235
+ return repo , nil
236
+ }
237
+
238
+ // Load the cached repository index to ensure it passes validation
239
+ if err := chartRepo .LoadFromCache (); err != nil {
240
+ return sourcev1 .HelmRepositoryNotReady (repo , sourcev1 .IndexationFailedReason , err .Error ()), err
231
241
}
242
+ // The repository checksum is the SHA256 of the loaded bytes, after sorting
243
+ artifact .Revision = chartRepo .Checksum
244
+ chartRepo .Unload ()
232
245
233
- // create artifact dir
246
+ // Create artifact dir
234
247
err = r .Storage .MkdirAll (artifact )
235
248
if err != nil {
236
249
err = fmt .Errorf ("unable to create repository index directory: %w" , err )
237
- return sourcev1 .HelmRepositoryNotReady (repository , sourcev1 .StorageOperationFailedReason , err .Error ()), err
250
+ return sourcev1 .HelmRepositoryNotReady (repo , sourcev1 .StorageOperationFailedReason , err .Error ()), err
238
251
}
239
252
240
- // acquire lock
253
+ // Acquire lock
241
254
unlock , err := r .Storage .Lock (artifact )
242
255
if err != nil {
243
256
err = fmt .Errorf ("unable to acquire lock: %w" , err )
244
- return sourcev1 .HelmRepositoryNotReady (repository , sourcev1 .StorageOperationFailedReason , err .Error ()), err
257
+ return sourcev1 .HelmRepositoryNotReady (repo , sourcev1 .StorageOperationFailedReason , err .Error ()), err
245
258
}
246
259
defer unlock ()
247
260
248
- // save artifact to storage
249
- if err := r .Storage .AtomicWriteFile (& artifact , bytes .NewReader (indexBytes ), 0644 ); err != nil {
250
- err = fmt .Errorf ("unable to write repository index file: %w" , err )
251
- return sourcev1 .HelmRepositoryNotReady (repository , sourcev1 .StorageOperationFailedReason , err .Error ()), err
261
+ // Save artifact to storage
262
+ if err = r .Storage .CopyFromPath (& artifact , chartRepo .CachePath ); err != nil {
263
+ return sourcev1 .HelmRepositoryNotReady (repo , sourcev1 .StorageOperationFailedReason , err .Error ()), err
252
264
}
253
265
254
- // update index symlink
266
+ // Update index symlink
255
267
indexURL , err := r .Storage .Symlink (artifact , "index.yaml" )
256
268
if err != nil {
257
269
err = fmt .Errorf ("storage error: %w" , err )
258
- return sourcev1 .HelmRepositoryNotReady (repository , sourcev1 .StorageOperationFailedReason , err .Error ()), err
270
+ return sourcev1 .HelmRepositoryNotReady (repo , sourcev1 .StorageOperationFailedReason , err .Error ()), err
259
271
}
260
272
261
273
message := fmt .Sprintf ("Fetched revision: %s" , artifact .Revision )
262
- return sourcev1 .HelmRepositoryReady (repository , artifact , indexURL , sourcev1 .IndexationSucceededReason , message ), nil
274
+ return sourcev1 .HelmRepositoryReady (repo , artifact , indexURL , sourcev1 .IndexationSucceededReason , message ), nil
263
275
}
264
276
265
277
func (r * HelmRepositoryReconciler ) reconcileDelete (ctx context.Context , repository sourcev1.HelmRepository ) (ctrl.Result , error ) {
0 commit comments