ClusterManager: Implements support of remote clusters.
After this change users will be able to:
* Create cluster and add/remove servers to it
* Install apps on remote cluster
* Move already installed apps between clusters
* Apps running on server being removed will auto-migrate
to another server from that same cluster
This is achieved by:
* Installing and running minimal version of dodo on remote cluster
* Ingress-nginx is installed automatically on new clusters
* Next to nginx we run VPN client in the same pod, so that
default cluster can establish secure communication with it
* Multiple reverse proxies are configured to get to the
remote cluster service from ingress installed on default cluster.
Next steps:
* Support remote clusters in dodo apps (prototype ready)
* Clean up old cluster when moving app to the new one. Currently
old cluster keeps running app pods even though no ingress can
reach it anymore.
Change-Id: Iffc908c93416d4126a8e1c2832eae7b659cb8044
diff --git a/core/installer/tasks/install.go b/core/installer/tasks/install.go
index 8b5bef7..6ff939b 100644
--- a/core/installer/tasks/install.go
+++ b/core/installer/tasks/install.go
@@ -1,7 +1,12 @@
package tasks
import (
+ "fmt"
+ "path/filepath"
+
"github.com/giolekva/pcloud/core/installer"
+ "github.com/giolekva/pcloud/core/installer/cluster"
+ "github.com/giolekva/pcloud/core/installer/soft"
)
type InstallFunc func() (installer.ReleaseResources, error)
@@ -50,3 +55,144 @@
t := newParentTask("Installing application", true, start, d)
return &t
}
+
+func NewClusterInitTask(m cluster.Manager, server cluster.Server, cnc installer.ClusterNetworkConfigurator, repo soft.RepoIO, setupFn cluster.ClusterSetupFunc) Task {
+ d := &dynamicTaskSlice{t: []Task{}}
+ done := make(chan error)
+ setupTask := newLeafTask(fmt.Sprintf("Installing dodo on %s", server.IP.String()), func() error {
+ _, err := m.Init(server, setupFn)
+ return err
+ })
+ d.Append(&setupTask)
+ setupTask.OnDone(func(err error) {
+ if err != nil {
+ done <- err
+ return
+ }
+ if err := cnc.AddCluster(m.State().Name, m.State().IngressIP); err != nil {
+ done <- err
+ return
+ }
+ _, err = repo.Do(func(fs soft.RepoFS) (string, error) {
+ if err := soft.WriteJson(fs, fmt.Sprintf("/clusters/%s/config.json", m.State().Name), m.State()); err != nil {
+ return "", err
+ }
+ return fmt.Sprintf("add server to cluster: %s", m.State().Name), nil
+ })
+ done <- err
+ })
+ start := func() error {
+ setupTask.Start()
+ return <-done
+ }
+ t := newParentTask("Installing application", true, start, d)
+ return &t
+}
+
+func NewRemoveClusterTask(m cluster.Manager, cnc installer.ClusterNetworkConfigurator,
+ repo soft.RepoIO) Task {
+ t := newLeafTask(fmt.Sprintf("Removing %s cluster", m.State().Name), func() error {
+ if err := cnc.RemoveCluster(m.State().Name, m.State().IngressIP); err != nil {
+ return err
+ }
+ _, err := repo.Do(func(fs soft.RepoFS) (string, error) {
+ if err := fs.RemoveAll(fmt.Sprintf("/clusters/%s", m.State().Name)); err != nil {
+ return "", err
+ }
+ kustPath := filepath.Join("/clusters", "kustomization.yaml")
+ kust, err := soft.ReadKustomization(fs, kustPath)
+ if err != nil {
+ return "", err
+ }
+ kust.RemoveResources(m.State().Name)
+ soft.WriteYaml(fs, kustPath, kust)
+ return fmt.Sprintf("remove cluster: %s", m.State().Name), nil
+ })
+ return err
+ })
+ return &t
+}
+
+func NewClusterJoinControllerTask(m cluster.Manager, server cluster.Server, repo soft.RepoIO) Task {
+ d := &dynamicTaskSlice{t: []Task{}}
+ done := make(chan error)
+ setupTask := newLeafTask(fmt.Sprintf("Joining %s to %s cluster", server.IP.String(), m.State().Name), func() error {
+ return m.JoinController(server)
+ })
+ d.Append(&setupTask)
+ setupTask.OnDone(func(err error) {
+ if err != nil {
+ done <- err
+ return
+ }
+ _, err = repo.Do(func(fs soft.RepoFS) (string, error) {
+ if err := soft.WriteJson(fs, fmt.Sprintf("/clusters/%s/config.json", m.State().Name), m.State()); err != nil {
+ return "", err
+ }
+ return fmt.Sprintf("add controller server to cluster: %s", m.State().Name), nil
+ })
+ done <- err
+ })
+ start := func() error {
+ setupTask.Start()
+ return <-done
+ }
+ t := newParentTask("Installing application", true, start, d)
+ return &t
+}
+
+func NewClusterJoinWorkerTask(m cluster.Manager, server cluster.Server, repo soft.RepoIO) Task {
+ d := &dynamicTaskSlice{t: []Task{}}
+ done := make(chan error)
+ setupTask := newLeafTask(fmt.Sprintf("Joining %s to %s cluster", server.IP.String(), m.State().Name), func() error {
+ return m.JoinWorker(server)
+ })
+ d.Append(&setupTask)
+ setupTask.OnDone(func(err error) {
+ if err != nil {
+ done <- err
+ return
+ }
+ _, err = repo.Do(func(fs soft.RepoFS) (string, error) {
+ if err := soft.WriteJson(fs, fmt.Sprintf("/clusters/%s/config.json", m.State().Name), m.State()); err != nil {
+ return "", err
+ }
+ return fmt.Sprintf("add worker server to cluster: %s", m.State().Name), nil
+ })
+ done <- err
+ })
+ start := func() error {
+ setupTask.Start()
+ return <-done
+ }
+ t := newParentTask("Installing application", true, start, d)
+ return &t
+}
+
+func NewClusterRemoveServerTask(m cluster.Manager, server string, repo soft.RepoIO) Task {
+ d := &dynamicTaskSlice{t: []Task{}}
+ done := make(chan error)
+ setupTask := newLeafTask(fmt.Sprintf("Removing %s from %s cluster", server, m.State().Name), func() error {
+ return m.RemoveServer(server)
+ })
+ d.Append(&setupTask)
+ setupTask.OnDone(func(err error) {
+ if err != nil {
+ done <- err
+ return
+ }
+ _, err = repo.Do(func(fs soft.RepoFS) (string, error) {
+ if err := soft.WriteJson(fs, fmt.Sprintf("/clusters/%s/config.json", m.State().Name), m.State()); err != nil {
+ return "", err
+ }
+ return fmt.Sprintf("remove %s from cluster: %s", server, m.State().Name), nil
+ })
+ done <- err
+ })
+ start := func() error {
+ setupTask.Start()
+ return <-done
+ }
+ t := newParentTask("Installing application", true, start, d)
+ return &t
+}