AppManager: App installation status monitoring

Change-Id: I64f4ae0d27892b74f8827a275907cb75da09a758
diff --git a/core/installer/status/helm.go b/core/installer/status/helm.go
new file mode 100644
index 0000000..b2db1d4
--- /dev/null
+++ b/core/installer/status/helm.go
@@ -0,0 +1,67 @@
+package status
+
+import (
+	"context"
+	"encoding/json"
+
+	"github.com/giolekva/pcloud/core/installer/kube"
+
+	"k8s.io/apimachinery/pkg/api/errors"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	"k8s.io/client-go/dynamic"
+)
+
+type helmRelease struct {
+	Status struct {
+		Conditions []struct {
+			Type   string `json:"type"`
+			Status string `json:"status"`
+		} `json:"conditions"`
+	} `json:"status,omitempty"`
+}
+
+type helmReleaseMonitor struct {
+	d dynamic.Interface
+}
+
+func (m *helmReleaseMonitor) Get(ref ResourceRef) (Status, error) {
+	ctx := context.Background()
+	res, err := m.d.Resource(
+		schema.GroupVersionResource{
+			Group:    "helm.toolkit.fluxcd.io",
+			Version:  "v2beta1",
+			Resource: "helmreleases",
+		},
+	).Namespace(ref.Namespace).Get(ctx, ref.Name, metav1.GetOptions{})
+	if err != nil {
+		if errors.IsNotFound(err) {
+			return StatusNotFound, nil
+		}
+		return StatusNoStatus, err
+	}
+	b, err := res.MarshalJSON()
+	if err != nil {
+		return StatusNoStatus, err
+	}
+	var hr helmRelease
+	if err := json.Unmarshal(b, &hr); err != nil {
+		return StatusNoStatus, err
+	}
+	// TODO(gio): check more thoroughly
+	for _, c := range hr.Status.Conditions {
+		if c.Type == "Ready" && c.Status == "True" {
+			return StatusSuccess, nil
+		}
+	}
+	return StatusProcessing, nil
+}
+
+func NewHelmReleaseMonitor(kubeconfig string) (ResourceMonitor, error) {
+	c, err := kube.NewKubeClient(kube.KubeConfigOpts{KubeConfigPath: kubeconfig})
+	if err != nil {
+		return nil, err
+	}
+	d := dynamic.New(c.RESTClient())
+	return &helmReleaseMonitor{d}, nil
+}