mirror of
				https://github.com/fluxcd/flux2.git
				synced 2025-10-31 08:17:19 +08:00 
			
		
		
		
	 e98f1142a6
			
		
	
	e98f1142a6
	
	
	
		
			
			When a user provided the `--ca-file` flag to the `bootstrap` command, the given CA file wasn't taken into account for cloning the repository locally. It was just passed along to the CR that is created so Flux can make use of it when cloning the repository in-cluster. However, users may not want to add a custom CA to their local host's trust chain and may expect the `--ca-file` flag to be respected also for cloning the repository locally. This is what this commit accomplishes. closes #1775 Signed-off-by: Max Jonas Werner <mail@makk.es>
		
			
				
	
	
		
			219 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			219 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| /*
 | |
| 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 bootstrap
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"strings"
 | |
| 	"time"
 | |
| 
 | |
| 	corev1 "k8s.io/api/core/v1"
 | |
| 	apierr "k8s.io/apimachinery/pkg/api/errors"
 | |
| 	apimeta "k8s.io/apimachinery/pkg/api/meta"
 | |
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | |
| 	"k8s.io/apimachinery/pkg/types"
 | |
| 	"sigs.k8s.io/controller-runtime/pkg/client"
 | |
| 
 | |
| 	kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
 | |
| 	"github.com/fluxcd/pkg/apis/meta"
 | |
| 
 | |
| 	"github.com/fluxcd/flux2/pkg/manifestgen/install"
 | |
| 	"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
 | |
| 	"github.com/fluxcd/flux2/pkg/manifestgen/sync"
 | |
| )
 | |
| 
 | |
| var (
 | |
| 	ErrReconciledWithWarning = errors.New("reconciled with warning")
 | |
| )
 | |
| 
 | |
| type Reconciler interface {
 | |
| 	// ReconcileComponents reconciles the components by generating the
 | |
| 	// manifests with the provided values, committing them to Git and
 | |
| 	// pushing to remote if there are any changes, and applying them
 | |
| 	// to the cluster.
 | |
| 	ReconcileComponents(ctx context.Context, manifestsBase string, options install.Options, secretOpts sourcesecret.Options) error
 | |
| 
 | |
| 	// ReconcileSourceSecret reconciles the source secret by generating
 | |
| 	// a new secret with the provided values if the secret does not
 | |
| 	// already exists on the cluster, or if any of the configuration
 | |
| 	// options changed.
 | |
| 	ReconcileSourceSecret(ctx context.Context, options sourcesecret.Options) error
 | |
| 
 | |
| 	// ReconcileSyncConfig reconciles the sync configuration by generating
 | |
| 	// the sync manifests with the provided values, committing them to Git
 | |
| 	// and pushing to remote if there are any changes.
 | |
| 	ReconcileSyncConfig(ctx context.Context, options sync.Options) error
 | |
| 
 | |
| 	// ReportKustomizationHealth reports about the health of the
 | |
| 	// Kustomization synchronizing the components.
 | |
| 	ReportKustomizationHealth(ctx context.Context, options sync.Options, pollInterval, timeout time.Duration) error
 | |
| 
 | |
| 	// ReportComponentsHealth reports about the health for the components
 | |
| 	// and extra components in install.Options.
 | |
| 	ReportComponentsHealth(ctx context.Context, options install.Options, timeout time.Duration) error
 | |
| }
 | |
| 
 | |
| type RepositoryReconciler interface {
 | |
| 	// ReconcileRepository reconciles an external Git repository.
 | |
| 	ReconcileRepository(ctx context.Context) error
 | |
| }
 | |
| 
 | |
| type PostGenerateSecretFunc func(ctx context.Context, secret corev1.Secret, options sourcesecret.Options) error
 | |
| 
 | |
| func Run(ctx context.Context, reconciler Reconciler, manifestsBase string,
 | |
| 	installOpts install.Options, secretOpts sourcesecret.Options, syncOpts sync.Options,
 | |
| 	pollInterval, timeout time.Duration) error {
 | |
| 
 | |
| 	var err error
 | |
| 	if r, ok := reconciler.(RepositoryReconciler); ok {
 | |
| 		if err = r.ReconcileRepository(ctx); err != nil && !errors.Is(err, ErrReconciledWithWarning) {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if err := reconciler.ReconcileComponents(ctx, manifestsBase, installOpts, secretOpts); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if err := reconciler.ReconcileSourceSecret(ctx, secretOpts); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if err := reconciler.ReconcileSyncConfig(ctx, syncOpts); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	var healthErrCount int
 | |
| 	if err := reconciler.ReportKustomizationHealth(ctx, syncOpts, pollInterval, timeout); err != nil {
 | |
| 		healthErrCount++
 | |
| 	}
 | |
| 	if err := reconciler.ReportComponentsHealth(ctx, installOpts, timeout); err != nil {
 | |
| 		healthErrCount++
 | |
| 	}
 | |
| 	if healthErrCount > 0 {
 | |
| 		// Composing a "smart" error message here from the returned
 | |
| 		// errors does not result in any useful information for the
 | |
| 		// user, as both methods log the failures they run into.
 | |
| 		err = fmt.Errorf("bootstrap failed with %d health check failure(s)", healthErrCount)
 | |
| 	}
 | |
| 
 | |
| 	return err
 | |
| }
 | |
| 
 | |
| func mustInstallManifests(ctx context.Context, kube client.Client, namespace string) bool {
 | |
| 	namespacedName := types.NamespacedName{
 | |
| 		Namespace: namespace,
 | |
| 		Name:      namespace,
 | |
| 	}
 | |
| 	var k kustomizev1.Kustomization
 | |
| 	if err := kube.Get(ctx, namespacedName, &k); err != nil {
 | |
| 		return true
 | |
| 	}
 | |
| 	return k.Status.LastAppliedRevision == ""
 | |
| }
 | |
| 
 | |
| func secretExists(ctx context.Context, kube client.Client, objKey client.ObjectKey) (bool, error) {
 | |
| 	if err := kube.Get(ctx, objKey, &corev1.Secret{}); err != nil {
 | |
| 		if apierr.IsNotFound(err) {
 | |
| 			return false, nil
 | |
| 		}
 | |
| 		return false, err
 | |
| 	}
 | |
| 	return true, nil
 | |
| }
 | |
| 
 | |
| func reconcileSecret(ctx context.Context, kube client.Client, secret corev1.Secret) error {
 | |
| 	objKey := client.ObjectKeyFromObject(&secret)
 | |
| 	var existing corev1.Secret
 | |
| 	err := kube.Get(ctx, objKey, &existing)
 | |
| 	if err != nil {
 | |
| 		if apierr.IsNotFound(err) {
 | |
| 			return kube.Create(ctx, &secret)
 | |
| 		}
 | |
| 		return err
 | |
| 	}
 | |
| 	existing.StringData = secret.StringData
 | |
| 	return kube.Update(ctx, &existing)
 | |
| }
 | |
| 
 | |
| func kustomizationPathDiffers(ctx context.Context, kube client.Client, objKey client.ObjectKey, path string) (string, error) {
 | |
| 	var k kustomizev1.Kustomization
 | |
| 	if err := kube.Get(ctx, objKey, &k); err != nil {
 | |
| 		if apierr.IsNotFound(err) {
 | |
| 			return "", nil
 | |
| 		}
 | |
| 		return "", err
 | |
| 	}
 | |
| 	normalizePath := func(p string) string {
 | |
| 		return fmt.Sprintf("./%s", strings.TrimPrefix(p, "./"))
 | |
| 	}
 | |
| 	if normalizePath(path) == normalizePath(k.Spec.Path) {
 | |
| 		return "", nil
 | |
| 	}
 | |
| 	return k.Spec.Path, nil
 | |
| }
 | |
| 
 | |
| func kustomizationReconciled(ctx context.Context, kube client.Client, objKey client.ObjectKey,
 | |
| 	kustomization *kustomizev1.Kustomization, expectRevision string) func() (bool, error) {
 | |
| 
 | |
| 	return func() (bool, error) {
 | |
| 		if err := kube.Get(ctx, objKey, kustomization); err != nil {
 | |
| 			return false, err
 | |
| 		}
 | |
| 
 | |
| 		// Detect suspended Kustomization, as this would result in an endless wait
 | |
| 		if kustomization.Spec.Suspend {
 | |
| 			return false, fmt.Errorf("Kustomization is suspended")
 | |
| 		}
 | |
| 
 | |
| 		// Confirm the state we are observing is for the current generation
 | |
| 		if kustomization.Generation != kustomization.Status.ObservedGeneration {
 | |
| 			return false, nil
 | |
| 		}
 | |
| 
 | |
| 		// Confirm the given revision has been attempted by the controller
 | |
| 		if kustomization.Status.LastAttemptedRevision != expectRevision {
 | |
| 			return false, nil
 | |
| 		}
 | |
| 
 | |
| 		// Confirm the resource is healthy
 | |
| 		if c := apimeta.FindStatusCondition(kustomization.Status.Conditions, meta.ReadyCondition); c != nil {
 | |
| 			switch c.Status {
 | |
| 			case metav1.ConditionTrue:
 | |
| 				return true, nil
 | |
| 			case metav1.ConditionFalse:
 | |
| 				return false, fmt.Errorf(c.Message)
 | |
| 			}
 | |
| 		}
 | |
| 		return false, nil
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func retry(retries int, wait time.Duration, fn func() error) (err error) {
 | |
| 	for i := 0; ; i++ {
 | |
| 		err = fn()
 | |
| 		if err == nil {
 | |
| 			return
 | |
| 		}
 | |
| 		if i >= retries {
 | |
| 			break
 | |
| 		}
 | |
| 		time.Sleep(wait)
 | |
| 	}
 | |
| 	return err
 | |
| }
 |