Reconciler: Reconcile dodo Apps as well

Fix service-account assignment.

Change-Id: Ic66c46ae5b728e09ee9118f61e31ab860623bce0
diff --git a/charts/fluxcd-reconciler/templates/install.yaml b/charts/fluxcd-reconciler/templates/install.yaml
index 29ce6ef..1ee3e95 100644
--- a/charts/fluxcd-reconciler/templates/install.yaml
+++ b/charts/fluxcd-reconciler/templates/install.yaml
@@ -10,7 +10,7 @@
   name: fluxcd-reconciler
 rules: # TODO(gio): restrict to ns create and dnszone get
 - apiGroups:
-  - "source.toolkit.fluxcd.io/v1"
+  - "source.toolkit.fluxcd.io"
   resources:
   - "gitrepositories"
   verbs:
@@ -62,12 +62,12 @@
     matchLabels:
       app: fluxcd-reconciler
   replicas: 1
-  serviceAccountName: fluxcd-reconciler
   template:
     metadata:
       labels:
         app: fluxcd-reconciler
     spec:
+      serviceAccountName: fluxcd-reconciler
       containers:
       - name: fluxcd-reconciler
         image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
diff --git a/core/fluxcd-reconciler/Makefile b/core/fluxcd-reconciler/Makefile
index 70f5889..0ef3b6c 100644
--- a/core/fluxcd-reconciler/Makefile
+++ b/core/fluxcd-reconciler/Makefile
@@ -1,3 +1,9 @@
+repo_name ?= giolekva
+podman ?= docker
+ifeq ($($(podman)), $(podman))
+manifest_dest=docker://docker.io/$(repo_name)/fluxcd-reconciler:latest
+endif
+
 clean:
 	rm -rf tmp
 	rm -f fluxcd_reconciler fluxcd_reconciler_*
@@ -27,18 +33,18 @@
 push_arm64: clean build_arm64
 	mkdir tmp
 	cp -r ../../charts tmp/
-	podman build --platform linux/arm64 --tag=giolekva/fluxcd-reconciler:arm64 .
+	$(podman) build --platform linux/arm64 --tag=$(repo_name)/fluxcd-reconciler:arm64 .
 	rm -rf tmp
-	podman push giolekva/fluxcd-reconciler:arm64
+	$(podman) push $(repo_name)/fluxcd-reconciler:arm64
 
 push_amd64: clean build_amd64
 	mkdir tmp
 	cp -r ../../charts tmp/
-	podman build --platform linux/amd64 --tag=giolekva/fluxcd-reconciler:amd64 .
+	$(podman) build --platform linux/amd64 --tag=$(repo_name)/fluxcd-reconciler:amd64 .
 	rm -rf tmp
-	podman push giolekva/fluxcd-reconciler:amd64
+	$(podman) push $(repo_name)/fluxcd-reconciler:amd64
 
 push: push_arm64 push_amd64
-	podman manifest create giolekva/fluxcd-reconciler:latest giolekva/fluxcd-reconciler:arm64 giolekva/fluxcd-reconciler:amd64
-	podman manifest push giolekva/fluxcd-reconciler:latest docker://docker.io/giolekva/fluxcd-reconciler:latest
-	podman manifest rm giolekva/fluxcd-reconciler:latest
+	$(podman) manifest create $(repo_name)/fluxcd-reconciler:latest $(repo_name)/fluxcd-reconciler:arm64 $(repo_name)/fluxcd-reconciler:amd64
+	$(podman) manifest push $(repo_name)/fluxcd-reconciler:latest $(manifest_dest)
+	$(podman) manifest rm $(repo_name)/fluxcd-reconciler:latest
diff --git a/core/fluxcd-reconciler/main.go b/core/fluxcd-reconciler/main.go
index 9220fec..c308b82 100644
--- a/core/fluxcd-reconciler/main.go
+++ b/core/fluxcd-reconciler/main.go
@@ -66,13 +66,19 @@
 	namespace string,
 	name string,
 ) error {
+	fmt.Printf("%+v %s %s\n", res, namespace, name)
 	unstr, err := client.Resource(res).Namespace(namespace).Get(context.TODO(), name, metav1.GetOptions{})
 	if err != nil {
 		return err
 	}
 	timeNowTime := time.Now()
 	annotations := unstr.GetAnnotations()
+	if annotations == nil {
+		annotations = map[string]string{}
+		unstr.SetAnnotations(annotations)
+	}
 	annotations[reconcileAnnotation] = timeNowTime.Format(reconcileAtLayout)
+	fmt.Printf("New reconciled at: %+v\n", annotations[reconcileAnnotation])
 	unstr.SetAnnotations(annotations)
 	unstr, err = client.Resource(res).Namespace(namespace).Update(context.TODO(), unstr, metav1.UpdateOptions{})
 	if err != nil {
@@ -85,14 +91,14 @@
 		}
 		reconciledAt, err := getReconciledAt(unstr)
 		if err != nil {
-			return err
+			continue
 		}
 		reconciledAtTime, err := time.Parse(reconcileAtLayout, reconciledAt)
 		if err != nil {
 			return err
 		}
-		reconciledAtTime = reconciledAtTime.Add(3 * time.Hour)
-		if reconciledAtTime.After(timeNowTime) {
+		fmt.Printf("Current reconciled at: %s\n", reconciledAtTime.Format(reconcileAtLayout))
+		if reconciledAtTime.Equal(timeNowTime) || reconciledAtTime.After(timeNowTime) {
 			return nil
 		}
 	}
@@ -116,7 +122,8 @@
 		Resource: "gitrepositories",
 	}
 	if err := reconcile(s.client, res, namespace, name); err != nil {
-		http.Error(w, "error", http.StatusInternalServerError)
+		fmt.Println(err)
+		http.Error(w, err.Error(), http.StatusInternalServerError)
 		return
 	}
 }
@@ -139,7 +146,8 @@
 		Resource: "kustomizations",
 	}
 	if err := reconcile(s.client, res, namespace, name); err != nil {
-		http.Error(w, "error", http.StatusInternalServerError)
+		fmt.Println(err)
+		http.Error(w, err.Error(), http.StatusInternalServerError)
 		return
 	}
 }
diff --git a/core/installer/cmd/app_manager.go b/core/installer/cmd/app_manager.go
index 33f122d..c03c83e 100644
--- a/core/installer/cmd/app_manager.go
+++ b/core/installer/cmd/app_manager.go
@@ -117,10 +117,7 @@
 		appManagerFlags.port,
 		m,
 		r,
-		tasks.NewFluxcdReconciler(
-			"http://fluxcd-reconciler.dodo-fluxcd-reconciler.svc.cluster.local",
-			env.Id,
-		),
+		tasks.NewFixedReconciler(env.Id, env.Id),
 		helmMon,
 	)
 	if err != nil {
diff --git a/core/installer/cmd/dodo_app.go b/core/installer/cmd/dodo_app.go
index ef62f5c..f61ee78 100644
--- a/core/installer/cmd/dodo_app.go
+++ b/core/installer/cmd/dodo_app.go
@@ -8,6 +8,7 @@
 
 	"github.com/giolekva/pcloud/core/installer"
 	"github.com/giolekva/pcloud/core/installer/soft"
+	"github.com/giolekva/pcloud/core/installer/tasks"
 	"github.com/giolekva/pcloud/core/installer/welcome"
 
 	_ "github.com/ncruces/go-sqlite3"
@@ -186,6 +187,12 @@
 	} else {
 		ug = welcome.NewInternalUserGetter()
 	}
+	reconciler := &tasks.SequentialReconciler{
+		[]tasks.Reconciler{
+			&tasks.SourceGitReconciler{},
+			// &tasks.KustomizationReconciler{},
+		},
+	}
 	s, err := welcome.NewDodoAppServer(
 		st,
 		nf,
@@ -204,6 +211,7 @@
 		env,
 		dodoAppFlags.external,
 		dodoAppFlags.fetchUsersAddr,
+		reconciler,
 	)
 	if err != nil {
 		return err
diff --git a/core/installer/tasks/env.go b/core/installer/tasks/env.go
index 20528a3..bc104cc 100644
--- a/core/installer/tasks/env.go
+++ b/core/installer/tasks/env.go
@@ -72,12 +72,12 @@
 	t.OnDone(func(_ error) {
 		done()
 	})
-	pr := NewFluxcdReconciler( // TODO(gio): make reconciler address a flag
-		"http://fluxcd-reconciler.dodo-fluxcd-reconciler.svc.cluster.local",
+	pr := NewFixedReconciler(
+		fmt.Sprintf("%s-flux", env.InfraName),
 		fmt.Sprintf("%s-flux", env.InfraName),
 	)
-	er := NewFluxcdReconciler(
-		"http://fluxcd-reconciler.dodo-fluxcd-reconciler.svc.cluster.local",
+	er := NewFixedReconciler(
+		env.Id,
 		env.Id,
 	)
 	go pr.Reconcile(rctx)
diff --git a/core/installer/tasks/reconciler.go b/core/installer/tasks/reconciler.go
index 0e8133e..9f8a004 100644
--- a/core/installer/tasks/reconciler.go
+++ b/core/installer/tasks/reconciler.go
@@ -4,35 +4,62 @@
 	"context"
 	"fmt"
 	"net/http"
-	"time"
 )
 
+// TODO(gio): make reconciler address a flag
+const baseURL = "http://fluxcd-reconciler.dodo-fluxcd-reconciler.svc.cluster.local"
+
 type Reconciler interface {
-	Reconcile(ctx context.Context)
+	Reconcile(ctx context.Context, namespace, name string)
 }
 
-type fluxcdReconciler struct {
-	resources []string
+type SequentialReconciler struct {
+	Reconcilers []Reconciler
 }
 
-func NewFluxcdReconciler(addr, name string) Reconciler {
-	return fluxcdReconciler{
-		resources: []string{
-			fmt.Sprintf("%s/source/git/%s/%s/reconcile", addr, name, name),
-			fmt.Sprintf("%s/kustomization/%s/%s/reconcile", addr, name, name),
-		},
+func (r *SequentialReconciler) Reconcile(ctx context.Context, namespace, name string) {
+	for _, rec := range r.Reconcilers {
+		rec.Reconcile(ctx, namespace, name)
 	}
 }
 
-func (r fluxcdReconciler) Reconcile(ctx context.Context) {
-	for {
-		select {
-		case <-time.After(3 * time.Second):
-			for _, res := range r.resources {
-				http.Get(res)
-			}
-		case <-ctx.Done():
-			return
-		}
+type SourceGitReconciler struct{}
+
+func (c SourceGitReconciler) Reconcile(ctx context.Context, namespace, name string) {
+	addr := fmt.Sprintf("%s/source/git/%s/%s/reconcile", baseURL, namespace, name)
+	http.Get(addr)
+}
+
+type KustomizationReconciler struct{}
+
+func (c KustomizationReconciler) Reconcile(ctx context.Context, namespace, name string) {
+	addr := fmt.Sprintf("%s/kustomization/%s/%s/reconcile", baseURL, namespace, name)
+	http.Get(addr)
+}
+
+type namespaceNamePair struct {
+	namespace string
+	name      string
+}
+
+type FixedReconciler struct {
+	namespace  string
+	name       string
+	reconciler Reconciler
+}
+
+func NewFixedReconciler(namespace, name string) *FixedReconciler {
+	return &FixedReconciler{
+		namespace,
+		name,
+		&SequentialReconciler{[]Reconciler{
+			SourceGitReconciler{},
+			// NOTE(gio): synchronizing git repository auto-syncs root kustomization as well
+			// KustomizationReconciler{},
+		}},
 	}
 }
+
+func (r *FixedReconciler) Reconcile(ctx context.Context) {
+	r.reconciler.Reconcile(ctx, r.namespace, r.name)
+}
diff --git a/core/installer/welcome/appmanager.go b/core/installer/welcome/appmanager.go
index 4fecf36..82421e9 100644
--- a/core/installer/welcome/appmanager.go
+++ b/core/installer/welcome/appmanager.go
@@ -25,7 +25,7 @@
 	port       int
 	m          *installer.AppManager
 	r          installer.AppRepository
-	reconciler tasks.Reconciler
+	reconciler *tasks.FixedReconciler
 	h          installer.HelmReleaseMonitor
 	tasks      map[string]tasks.Task
 	ta         map[string]installer.EnvApp
@@ -64,7 +64,7 @@
 	port int,
 	m *installer.AppManager,
 	r installer.AppRepository,
-	reconciler tasks.Reconciler,
+	reconciler *tasks.FixedReconciler,
 	h installer.HelmReleaseMonitor,
 ) (*AppManagerServer, error) {
 	tmpl, err := parseTemplatesAppManager(appTmpls)
@@ -239,10 +239,13 @@
 	appDir := fmt.Sprintf("/apps/%s", instanceId)
 	namespace := fmt.Sprintf("%s%s%s", env.NamespacePrefix, a.Namespace(), suffix)
 	t := tasks.NewInstallTask(s.h, func() (installer.ReleaseResources, error) {
-		return s.m.Install(a, instanceId, appDir, namespace, values)
+		rr, err := s.m.Install(a, instanceId, appDir, namespace, values)
+		if err == nil {
+			ctx, _ := context.WithTimeout(context.Background(), 5*time.Second)
+			go s.reconciler.Reconcile(ctx)
+		}
+		return rr, err
 	})
-	ctx, _ := context.WithTimeout(context.Background(), 2*time.Minute)
-	go s.reconciler.Reconcile(ctx)
 	if _, ok := s.tasks[instanceId]; ok {
 		panic("MUST NOT REACH!")
 	}
diff --git a/core/installer/welcome/dodo_app.go b/core/installer/welcome/dodo_app.go
index fbf732b..a19fecf 100644
--- a/core/installer/welcome/dodo_app.go
+++ b/core/installer/welcome/dodo_app.go
@@ -21,6 +21,7 @@
 
 	"github.com/giolekva/pcloud/core/installer"
 	"github.com/giolekva/pcloud/core/installer/soft"
+	"github.com/giolekva/pcloud/core/installer/tasks"
 
 	"github.com/gorilla/mux"
 	"github.com/gorilla/securecookie"
@@ -106,6 +107,7 @@
 	appTmpls          AppTmplStore
 	external          bool
 	fetchUsersAddr    string
+	reconciler        tasks.Reconciler
 	logs              map[string]string
 }
 
@@ -133,6 +135,7 @@
 	env installer.EnvConfig,
 	external bool,
 	fetchUsersAddr string,
+	reconciler tasks.Reconciler,
 ) (*DodoAppServer, error) {
 	tmplts, err := parseTemplatesDodoApp(dodoAppTmplFS)
 	if err != nil {
@@ -169,6 +172,7 @@
 		appTmpls,
 		external,
 		fetchUsersAddr,
+		reconciler,
 		map[string]string{},
 	}
 	config, err := client.GetRepo(ConfigRepoName)
@@ -1004,6 +1008,8 @@
 			}
 		}()
 	}
+	ctx, _ := context.WithTimeout(context.Background(), 2*time.Minute)
+	go s.reconciler.Reconcile(ctx, s.namespace, "config")
 	return nil
 }
 
@@ -1035,29 +1041,28 @@
 	} `json:"input"`
 }
 
-func (s *DodoAppServer) updateDodoApp(appStatus installer.EnvApp, name, namespace string, networks []installer.Network) (installer.ReleaseResources, error) {
-	fmt.Println("111")
+func (s *DodoAppServer) updateDodoApp(
+	appStatus installer.EnvApp,
+	name, namespace string,
+	networks []installer.Network,
+) (installer.ReleaseResources, error) {
 	repo, err := s.client.GetRepo(name)
 	if err != nil {
 		return installer.ReleaseResources{}, err
 	}
-	fmt.Println("111")
 	hf := installer.NewGitHelmFetcher()
 	m, err := installer.NewAppManager(repo, s.nsc, s.jc, hf, "/.dodo")
 	if err != nil {
 		return installer.ReleaseResources{}, err
 	}
-	fmt.Println("111")
 	appCfg, err := soft.ReadFile(repo, "app.cue")
 	if err != nil {
 		return installer.ReleaseResources{}, err
 	}
-	fmt.Println("111")
 	app, err := installer.NewDodoApp(appCfg)
 	if err != nil {
 		return installer.ReleaseResources{}, err
 	}
-	fmt.Println("111")
 	lg := installer.GitRepositoryLocalChartGenerator{"app", namespace}
 	var ret installer.ReleaseResources
 	if _, err := repo.Do(func(r soft.RepoFS) (string, error) {
@@ -1079,16 +1084,13 @@
 			installer.WithLocalChartGenerator(lg),
 			installer.WithNoLock(),
 		)
-		fmt.Println("111")
 		if err != nil {
 			return "", err
 		}
-		fmt.Println("111")
 		var rendered dodoAppRendered
 		if err := json.NewDecoder(bytes.NewReader(ret.RenderedRaw)).Decode(&rendered); err != nil {
 			return "", nil
 		}
-		fmt.Println("111")
 		if _, err := m.Install(
 			appStatus,
 			"status",
@@ -1108,7 +1110,6 @@
 		); err != nil {
 			return "", err
 		}
-		fmt.Println("111")
 		return "install app", nil
 	},
 		soft.WithCommitToBranch("dodo"),
@@ -1116,6 +1117,8 @@
 	); err != nil {
 		return installer.ReleaseResources{}, err
 	}
+	ctx, _ := context.WithTimeout(context.Background(), 2*time.Minute)
+	go s.reconciler.Reconcile(ctx, namespace, "app")
 	return ret, nil
 }