diff --git a/api/v1alpha1/helmrepository_types.go b/api/v1alpha1/helmrepository_types.go index 07608a45..da7f2d2c 100644 --- a/api/v1alpha1/helmrepository_types.go +++ b/api/v1alpha1/helmrepository_types.go @@ -62,3 +62,8 @@ type HelmRepositoryList struct { func init() { SchemeBuilder.Register(&HelmRepository{}, &HelmRepositoryList{}) } + +const ( + IndexDownloadFailedReason string = "IndexDownloadFailed" + IndexDownloadSucceedReason string = "IndexDownloadSucceed" +) diff --git a/controllers/helmrepository_controller.go b/controllers/helmrepository_controller.go index eeded0dc..eaeaad22 100644 --- a/controllers/helmrepository_controller.go +++ b/controllers/helmrepository_controller.go @@ -18,13 +18,22 @@ package controllers import ( "context" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "path" + "time" "github.com/go-logr/logr" + "gopkg.in/yaml.v2" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - sourcerv1alpha1 "github.com/fluxcd/sourcer/api/v1alpha1" + sourcerv1 "github.com/fluxcd/sourcer/api/v1alpha1" ) // HelmRepositoryReconciler reconciles a HelmRepository object @@ -38,16 +47,101 @@ type HelmRepositoryReconciler struct { // +kubebuilder:rbac:groups=sourcer.fluxcd.io,resources=helmrepositories/status,verbs=get;update;patch func (r *HelmRepositoryReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { - _ = context.Background() - _ = r.Log.WithValues("helmrepository", req.NamespacedName) + ctx := context.Background() + log := r.Log.WithValues("helmrepository", req.NamespacedName) - // your logic here + var repo sourcerv1.HelmRepository - return ctrl.Result{}, nil + if err := r.Get(ctx, req.NamespacedName, &repo); err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + readyCondition := sourcerv1.RepositoryCondition{ + Type: sourcerv1.RepositoryConditionReady, + Status: corev1.ConditionUnknown, + } + + if len(repo.Status.Conditions) == 0 { + log.Info("Starting index download") + repo.Status.Conditions = []sourcerv1.RepositoryCondition{readyCondition} + if err := r.Status().Update(ctx, &repo); err != nil { + log.Error(err, "unable to update HelmRepository status") + return ctrl.Result{}, err + } + } else { + for _, condition := range repo.Status.Conditions { + if condition.Type == sourcerv1.RepositoryConditionReady { + readyCondition = condition + break + } + } + } + + if err := downloadIndex(repo.Spec.Url); err != nil { + log.Info("Index download error", "error", err.Error()) + readyCondition.Reason = sourcerv1.IndexDownloadFailedReason + readyCondition.Message = err.Error() + readyCondition.Status = corev1.ConditionFalse + } else { + log.Info("Index download successful") + readyCondition.Reason = sourcerv1.IndexDownloadSucceedReason + readyCondition.Message = "Repository is ready" + readyCondition.Status = corev1.ConditionTrue + } + + timeNew := metav1.Now() + readyCondition.LastTransitionTime = &timeNew + repo.Status.Conditions = []sourcerv1.RepositoryCondition{readyCondition} + + if err := r.Status().Update(ctx, &repo); err != nil { + log.Error(err, "unable to update HelmRepository status") + return ctrl.Result{}, err + } + + log.Info("Sync finished") + return ctrl.Result{RequeueAfter: time.Minute}, nil } func (r *HelmRepositoryReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). - For(&sourcerv1alpha1.HelmRepository{}). + For(&sourcerv1.HelmRepository{}). Complete(r) } + +func downloadIndex(repoUrl string) error { + //time.Sleep(10 * time.Second) + parsedURL, err := url.Parse(repoUrl) + if err != nil { + return fmt.Errorf("unable to parse repository url %w", err) + } + parsedURL.RawPath = path.Join(parsedURL.RawPath, "index.yaml") + parsedURL.Path = path.Join(parsedURL.Path, "index.yaml") + indexURL := parsedURL.String() + + res, err := http.DefaultClient.Get(indexURL) + if err != nil { + return fmt.Errorf("unable to download repository index %w", err) + } + + defer res.Body.Close() + + if res.StatusCode > 300 { + return fmt.Errorf("unable to download repository index, respose status code %v", res.StatusCode) + } + + body, err := ioutil.ReadAll(res.Body) + if err != nil { + return fmt.Errorf("unable to read repository index %w", err) + } + + index := struct { + APIVersion string `json:"apiVersion"` + Generated time.Time `json:"generated"` + }{} + + if err := yaml.Unmarshal(body, &index); err != nil { + return fmt.Errorf("unable to unmarshal repository index %w", err) + } + + return nil +}