diff --git a/.travis.yml b/.travis.yml index 721db553..74bd9b03 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,15 @@ before_install: - go get -v github.com/golang/dep/cmd/dep - dep ensure - go get -v github.com/golang/lint/golint + - go get -u honnef.co/go/tools/... + - go get -u mvdan.cc/unparam + - go get -u github.com/client9/misspell/cmd/misspell script: - golint cmd/ pkg/ - go test -v ./... + - unused $(go list ./...) + - gosimple $(go list ./...) + - misspell -error $(git ls-files | grep -v vendor/) + - unparam $(go list ./...) + - staticcheck $(go list ./...) diff --git a/Gopkg.lock b/Gopkg.lock index 936b442a..935756ab 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -219,14 +219,6 @@ revision = "907c19d40d9a6c9bb55f040ff4ae45271a4754b9" version = "v1.1.0" -[[projects]] - branch = "master" - digest = "1:d2a36ba38f4f6871cd4346b5b3c327873c3b8da0a4c915ae82fba65c67a9a40c" - name = "github.com/thcyron/uiprogress" - packages = ["."] - pruneopts = "UT" - revision = "25e98ffb0e98b5192b475d8f2fd78083bfe9a67e" - [[projects]] branch = "master" digest = "1:10e257965f5774c1ef03b258130a6caa8dddbfe03041f69d7c16a00463dece62" @@ -292,7 +284,6 @@ "github.com/mitchellh/go-homedir", "github.com/spf13/cobra", "github.com/spf13/viper", - "github.com/thcyron/uiprogress", "golang.org/x/crypto/ssh", "golang.org/x/crypto/ssh/terminal", ] diff --git a/cmd/cluster_add_external_worker.go b/cmd/cluster_add_external_worker.go index 762ae125..2075f128 100644 --- a/cmd/cluster_add_external_worker.go +++ b/cmd/cluster_add_external_worker.go @@ -17,12 +17,13 @@ package cmd import ( "errors" "fmt" + "log" + "strings" + "github.com/spf13/cobra" "github.com/xetys/hetzner-kube/pkg" "github.com/xetys/hetzner-kube/pkg/clustermanager" "github.com/xetys/hetzner-kube/pkg/hetzner" - "log" - "strings" ) // clusterAddWorkerCmd represents the clusterAddWorker command @@ -138,9 +139,7 @@ An external server must meet the following requirements: } externalNode.PrivateIPAddress = fmt.Sprintf("%s.%d", cidrPrefix, nextNode) coordinator := pkg.NewProgressCoordinator() - hetznerProvider := hetzner.NewHetznerProvider(AppConf.Context, AppConf.Client, AppConf.CurrentContext.Token) - hetznerProvider.InitCluster(cluster.Name, cluster.NodeCIDR) - hetznerProvider.SetNodes(cluster.Nodes) + hetznerProvider := hetzner.NewHetznerProvider(AppConf.Context, AppConf.Client, *cluster, AppConf.CurrentContext.Token) clusterManager := clustermanager.NewClusterManagerFromCluster(*cluster, hetznerProvider, sshClient, coordinator) nodes := []clustermanager.Node{externalNode} diff --git a/cmd/cluster_add_worker.go b/cmd/cluster_add_worker.go index 3213a38e..442cdca4 100644 --- a/cmd/cluster_add_worker.go +++ b/cmd/cluster_add_worker.go @@ -93,7 +93,8 @@ You can specify the worker server type as in cluster create.`, } coordinator := pkg.NewProgressCoordinator() - hetznerProvider, clusterManager := hetzner.ProviderAndManager(AppConf.Context, *cluster, AppConf.Client, AppConf.SSHClient, coordinator, AppConf.CurrentContext.Token) + hetznerProvider := hetzner.NewHetznerProvider(AppConf.Context, AppConf.Client, *cluster, AppConf.CurrentContext.Token) + clusterManager := clustermanager.NewClusterManagerFromCluster(*cluster, hetznerProvider, AppConf.SSHClient, coordinator) err := AppConf.SSHClient.(*clustermanager.SSHCommunicator).CapturePassphrase(sshKeyName) if err != nil { log.Fatal(err) diff --git a/cmd/cluster_addon.go b/cmd/cluster_addon.go index 8ada4bb7..ed873aef 100644 --- a/cmd/cluster_addon.go +++ b/cmd/cluster_addon.go @@ -55,7 +55,7 @@ func validateAddonSubCommand(cmd *cobra.Command, args []string) error { return errors.New("exactly one argument expected") } addonName := args[0] - provider, _ := hetzner.ProviderAndManager(AppConf.Context, *cluster, AppConf.Client, AppConf.SSHClient, nil, AppConf.CurrentContext.Token) + provider := hetzner.NewHetznerProvider(AppConf.Context, AppConf.Client, *cluster, AppConf.CurrentContext.Token) addonService := addons.NewClusterAddonService(provider, AppConf.SSHClient) if !addonService.AddonExists(addonName) { return fmt.Errorf("addon %s not found", addonName) diff --git a/cmd/cluster_addon_install.go b/cmd/cluster_addon_install.go index e985cb47..6cff7f87 100644 --- a/cmd/cluster_addon_install.go +++ b/cmd/cluster_addon_install.go @@ -35,7 +35,7 @@ var clusterAddonInstallCmd = &cobra.Command{ _, cluster := AppConf.Config.FindClusterByName(name) log.Printf("installing addon %s", addonName) - provider, _ := hetzner.ProviderAndManager(AppConf.Context, *cluster, AppConf.Client, AppConf.SSHClient, nil, AppConf.CurrentContext.Token) + provider := hetzner.NewHetznerProvider(AppConf.Context, AppConf.Client, *cluster, AppConf.CurrentContext.Token) addonService := addons.NewClusterAddonService(provider, AppConf.SSHClient) masterNode, err := provider.GetMasterNode() FatalOnError(err) diff --git a/cmd/cluster_addon_list.go b/cmd/cluster_addon_list.go index 1efc24ea..d3b19f38 100644 --- a/cmd/cluster_addon_list.go +++ b/cmd/cluster_addon_list.go @@ -36,7 +36,7 @@ var clusterAddonListCmd = &cobra.Command{ fmt.Fprintln(tw, "NAME\tREQUIRES\tDESCRIPTION\tURL") cluster := &clustermanager.Cluster{Nodes: []clustermanager.Node{clustermanager.Node{IsMaster: true}}} - provider, _ := hetzner.ProviderAndManager(AppConf.Context, *cluster, AppConf.Client, AppConf.SSHClient, nil, AppConf.CurrentContext.Token) + provider := hetzner.NewHetznerProvider(AppConf.Context, AppConf.Client, *cluster, AppConf.CurrentContext.Token) addonService := addons.NewClusterAddonService(provider, AppConf.SSHClient) for _, addon := range addonService.Addons() { requires := "-" diff --git a/cmd/cluster_addon_uninstall.go b/cmd/cluster_addon_uninstall.go index 7481eded..78e9a115 100644 --- a/cmd/cluster_addon_uninstall.go +++ b/cmd/cluster_addon_uninstall.go @@ -35,7 +35,7 @@ var clusterAddonUninstallCmd = &cobra.Command{ _, cluster := AppConf.Config.FindClusterByName(name) log.Printf("removing addon %s", addonName) - provider, _ := hetzner.ProviderAndManager(AppConf.Context, *cluster, AppConf.Client, AppConf.SSHClient, nil, AppConf.CurrentContext.Token) + provider := hetzner.NewHetznerProvider(AppConf.Context, AppConf.Client, *cluster, AppConf.CurrentContext.Token) masterNode, err := provider.GetMasterNode() FatalOnError(err) diff --git a/cmd/cluster_create.go b/cmd/cluster_create.go index 582b1395..8b1b96db 100644 --- a/cmd/cluster_create.go +++ b/cmd/cluster_create.go @@ -79,18 +79,18 @@ func RunClusterCreate(cmd *cobra.Command, args []string) { workerServerType, _ := cmd.Flags().GetString("worker-server-type") datacenters, _ := cmd.Flags().GetStringSlice("datacenters") nodeCidr, _ := cmd.Flags().GetString("node-cidr") + cloudInit, _ := cmd.Flags().GetString("cloud-init") + + hetznerProvider := hetzner.NewHetznerProvider(AppConf.Context, AppConf.Client, clustermanager.Cluster{ + Name: clusterName, + NodeCIDR: nodeCidr, + CloudInitFile: cloudInit, + }, AppConf.CurrentContext.Token) - hetznerProvider := hetzner.NewHetznerProvider(AppConf.Context, AppConf.Client, AppConf.CurrentContext.Token) - hetznerProvider.InitCluster(clusterName, nodeCidr) sshClient := clustermanager.NewSSHCommunicator(AppConf.Config.SSHKeys) err := sshClient.(*clustermanager.SSHCommunicator).CapturePassphrase(sshKeyName) FatalOnError(err) - var cloudInit string - if cloudInit, _ = cmd.Flags().GetString("cloud-init"); cloudInit != "" { - hetznerProvider.SetCloudInitFile(cloudInit) - } - if haEnabled && isolatedEtcd { if _, err := hetznerProvider.CreateEtcdNodes(sshKeyName, masterServerType, datacenters, etcdCount); err != nil { log.Println(err) diff --git a/cmd/cluster_create_test.go b/cmd/cluster_create_test.go index 043fd4ca..3c38eb3d 100644 --- a/cmd/cluster_create_test.go +++ b/cmd/cluster_create_test.go @@ -1,8 +1,9 @@ package cmd import ( - "github.com/xetys/hetzner-kube/pkg/clustermanager" "testing" + + "github.com/xetys/hetzner-kube/pkg/clustermanager" ) func TestClusterCmdValidate(t *testing.T) { diff --git a/cmd/cluster_kubeconfig.go b/cmd/cluster_kubeconfig.go index 58a70694..a9cff7f7 100644 --- a/cmd/cluster_kubeconfig.go +++ b/cmd/cluster_kubeconfig.go @@ -46,7 +46,7 @@ Example 4: hetzner-kube cluster kubeconfig -n my-cluster -p > my-conf.yaml # pri name := args[0] _, cluster := AppConf.Config.FindClusterByName(name) - provider, _ := hetzner.ProviderAndManager(AppConf.Context, *cluster, AppConf.Client, AppConf.SSHClient, nil, AppConf.CurrentContext.Token) + provider := hetzner.NewHetznerProvider(AppConf.Context, AppConf.Client, *cluster, AppConf.CurrentContext.Token) masterNode, err := provider.GetMasterNode() FatalOnError(err) diff --git a/cmd/config.go b/cmd/config.go index 9c28c093..4fc99ab2 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -11,9 +11,7 @@ import ( "os/user" "path/filepath" - "github.com/go-kit/kit/log/term" "github.com/hetznercloud/hcloud-go/hcloud" - "github.com/thcyron/uiprogress" "github.com/xetys/hetzner-kube/pkg/clustermanager" ) @@ -180,34 +178,6 @@ func (app *AppConfig) DeleteContextByName(name string) error { return fmt.Errorf("context '%s' not found", name) } -// ActionProgress (deprecated) -func (app *AppConfig) ActionProgress(ctx context.Context, action *hcloud.Action) error { - errCh, progressCh := waitAction(ctx, app.Client, action) - - if term.IsTerminal(os.Stdout) { - progress := uiprogress.New() - - progress.Start() - bar := progress.AddBar(100).AppendCompleted().PrependElapsed() - bar.Empty = ' ' - - for { - select { - case err := <-errCh: - if err == nil { - bar.Set(100) - } - progress.Stop() - return err - case p := <-progressCh: - bar.Set(p) - } - } - } else { - return <-errCh - } -} - func (app *AppConfig) assertActiveContext() error { if app.CurrentContext == nil { return errors.New("no context selected") diff --git a/cmd/util.go b/cmd/util.go index a81b1dae..47494ade 100644 --- a/cmd/util.go +++ b/cmd/util.go @@ -1,105 +1,19 @@ package cmd import ( - "context" "fmt" "log" - "time" "github.com/Pallinder/go-randomdata" - "github.com/hetznercloud/hcloud-go/hcloud" ) -var sshPassPhrases = make(map[string][]byte) - -func waitAction(ctx context.Context, client *hcloud.Client, action *hcloud.Action) (<-chan error, <-chan int) { - errCh := make(chan error, 1) - progressCh := make(chan int) - - go func() { - defer close(errCh) - defer close(progressCh) - - ticker := time.NewTicker(100 * time.Millisecond) - - sendProgress := func(p int) { - select { - case progressCh <- p: - break - default: - break - } - } - - for { - select { - case <-ctx.Done(): - errCh <- ctx.Err() - return - case <-ticker.C: - break - } - - action, _, err := client.Action.GetByID(ctx, action.ID) - if err != nil { - errCh <- ctx.Err() - return - } - - switch action.Status { - case hcloud.ActionStatusRunning: - sendProgress(action.Progress) - break - case hcloud.ActionStatusSuccess: - sendProgress(100) - errCh <- nil - return - case hcloud.ActionStatusError: - errCh <- action.Error() - return - } - } - }() - - return errCh, progressCh -} - func randomName() string { return fmt.Sprintf("%s-%s%s", randomdata.Adjective(), randomdata.Noun(), randomdata.Adjective()) } -//Index find the index of an element int the array -func Index(vs []string, t string) int { - for i, v := range vs { - if v == t { - return i - } - } - return -1 -} - -//Include indicate if a string is in the strinc array -func Include(vs []string, t string) bool { - return Index(vs, t) >= 0 -} - //FatalOnError is an helper function to transform error to fatl func FatalOnError(err error) { if err != nil { log.Fatal(err) } } - -func waitOrError(tc chan bool, ec chan error, numProcPtr *int) error { - numProcs := *numProcPtr - for numProcs > 0 { - select { - case err := <-ec: - return err - case <-tc: - numProcs-- - } - } - - return nil -} diff --git a/pkg/clustermanager/cluster.go b/pkg/clustermanager/cluster.go index bf93b5e5..d511d34a 100644 --- a/pkg/clustermanager/cluster.go +++ b/pkg/clustermanager/cluster.go @@ -19,7 +19,6 @@ type Manager struct { eventService EventService nodeCommunicator NodeCommunicator clusterProvider ClusterProvider - wait bool } //NewClusterManager create a new manager for the cluster @@ -102,7 +101,10 @@ func (manager *Manager) ProvisionNodes(nodes []Node) error { func (manager *Manager) SetupEncryptedNetwork() error { nodes := manager.nodes // render a public/private key pair - keyPairs := manager.GenerateKeyPairs(nodes[0], len(nodes)) + keyPairs, err := manager.GenerateKeyPairs(nodes[0], len(nodes)) + if err != nil { + return fmt.Errorf("unable to setup encrypted network: %v", err) + } for i, keyPair := range keyPairs { manager.nodes[i].WireGuardKeyPair = keyPair @@ -135,7 +137,7 @@ func (manager *Manager) SetupEncryptedNetwork() error { }(node) } - err := waitOrError(trueChan, errChan, &numProc) + err = waitOrError(trueChan, errChan, &numProc) if err != nil { return err } @@ -166,7 +168,6 @@ func (manager *Manager) InstallMasters() error { numMaster := 0 for _, node := range manager.nodes { - if node.IsMaster { _, err := manager.nodeCommunicator.RunCmd(node, "kubeadm reset") if err != nil { @@ -314,21 +315,15 @@ func (manager *Manager) InstallEtcdNodes(nodes []Node) error { //InstallWorkers installs kubernetes workers to given nodes func (manager *Manager) InstallWorkers(nodes []Node) error { - var joinCommand string + node, err := manager.clusterProvider.GetMasterNode() + if err != nil { + return err + } + // create join command - for _, node := range manager.nodes { - if node.IsMaster { - for tries := 0; ; tries++ { - output, err := manager.nodeCommunicator.RunCmd(node, "kubeadm token create --print-join-command") - if tries < 10 && err != nil { - return err - } - time.Sleep(2 * time.Second) - joinCommand = output - break - } - break - } + joinCommand, err := manager.nodeCommunicator.RunCmd(*node, "kubeadm token create --print-join-command") + if err != nil { + return err } errChan := make(chan error) @@ -443,8 +438,12 @@ func (manager *Manager) DeployLoadBalancer(nodes []Node) error { errChan := make(chan error) trueChan := make(chan bool) numProcs := 0 - masterNodes := manager.clusterProvider.GetMasterNodes() - masterIps := strings.Join(Nodes2IPs(masterNodes), " ") + masterNodesIP := []string{} + for _, node := range manager.clusterProvider.GetMasterNodes() { + masterNodesIP = append(masterNodesIP, node.IPAddress) + } + + masterIps := strings.Join(masterNodesIP, " ") for _, node := range nodes { if !node.IsMaster && node.IsEtcd { continue diff --git a/pkg/clustermanager/ssh_communicator.go b/pkg/clustermanager/ssh_communicator.go index 8903a681..563cda33 100644 --- a/pkg/clustermanager/ssh_communicator.go +++ b/pkg/clustermanager/ssh_communicator.go @@ -121,10 +121,11 @@ func (sshComm *SSHCommunicator) WriteFile(node Node, filePath string, content st defer connection.Close() // log.Println("Connected succeeded!") session, err := connection.NewSession() - defer session.Close() if err != nil { log.Fatalf("session failed:%v", err) } + defer session.Close() + permission := "C0644" if executable { permission = "C0755" diff --git a/pkg/clustermanager/util.go b/pkg/clustermanager/util.go index 39bb75f1..7870ee92 100644 --- a/pkg/clustermanager/util.go +++ b/pkg/clustermanager/util.go @@ -1,7 +1,5 @@ package clustermanager -import "log" - func waitOrError(tc chan bool, ec chan error, numProcPtr *int) error { numProcs := *numProcPtr for numProcs > 0 { @@ -15,25 +13,3 @@ func waitOrError(tc chan bool, ec chan error, numProcPtr *int) error { return nil } - -//Node2IP get IP address for a node -func Node2IP(node Node) string { - return node.IPAddress -} - -//Nodes2IPs get the collection of IP addresses for a node -func Nodes2IPs(nodes []Node) []string { - ips := []string{} - for _, node := range nodes { - ips = append(ips, Node2IP(node)) - } - - return ips -} - -//FatalOnError is an helper function used to transfor error to fatal -func FatalOnError(err error) { - if err != nil { - log.Fatal(err) - } -} diff --git a/pkg/clustermanager/wireguard.go b/pkg/clustermanager/wireguard.go index b00d14f3..c16459ba 100644 --- a/pkg/clustermanager/wireguard.go +++ b/pkg/clustermanager/wireguard.go @@ -13,19 +13,23 @@ type WgKeyPair struct { } //GenerateKeyPairs generate key pairs -func (manager *Manager) GenerateKeyPairs(node Node, count int) []WgKeyPair { +func (manager *Manager) GenerateKeyPairs(node Node, count int) ([]WgKeyPair, error) { genKeyPairs := fmt.Sprintf(`echo "[" ;for i in {1..%d}; do pk=$(wg genkey); pubk=$(echo $pk | wg pubkey);echo "{\"private\":\"$pk\",\"public\":\"$pubk\"},"; done; echo "]";`, count) // gives an invalid JSON back o, err := manager.nodeCommunicator.RunCmd(node, genKeyPairs) - FatalOnError(err) + if err != nil { + return []WgKeyPair{}, fmt.Errorf("unable to generate a key pairs: %v", err) + } o = o[0:len(o)-4] + "]" // now it's a valid json var keyPairs []WgKeyPair err = json.Unmarshal([]byte(o), &keyPairs) - FatalOnError(err) + if err != nil { + return []WgKeyPair{}, fmt.Errorf("unable to json decode key pairs: %v", err) + } - return keyPairs + return keyPairs, nil } //GenerateWireguardConf generate wireguard configuration file diff --git a/pkg/hetzner/hetzner_provider.go b/pkg/hetzner/hetzner_provider.go index 93b5370a..84f8c25d 100644 --- a/pkg/hetzner/hetzner_provider.go +++ b/pkg/hetzner/hetzner_provider.go @@ -28,19 +28,16 @@ type Provider struct { } // NewHetznerProvider returns an instance of hetzner.Provider -func NewHetznerProvider(context context.Context, client *hcloud.Client, token string) *Provider { - return &Provider{client: client, context: context, token: token} -} - -// InitCluster provides cluster information for further creation -func (provider *Provider) InitCluster(clusterName, nodeCidr string) { - provider.clusterName = clusterName - provider.nodeCidr = nodeCidr -} - -// SetCloudInitFile sets cloud init file for node provisioning -func (provider *Provider) SetCloudInitFile(cloudInitFile string) { - provider.cloudInitFile = cloudInitFile +func NewHetznerProvider(context context.Context, client *hcloud.Client, cluster clustermanager.Cluster, token string) *Provider { + return &Provider{ + client: client, + context: context, + token: token, + nodeCidr: cluster.NodeCIDR, + clusterName: cluster.Name, + cloudInitFile: cluster.CloudInitFile, + nodes: cluster.Nodes, + } } // CreateNodes creates hetzner nodes @@ -80,8 +77,7 @@ func (provider *Provider) CreateNodes(suffix string, template clustermanager.Nod var nodes []clustermanager.Node for i := 1; i <= count; i++ { - var serverOpts hcloud.ServerCreateOpts - serverOpts = serverOptsTemplate + serverOpts := serverOptsTemplate nodeNumber := i + offset serverOpts.Name = strings.Replace(serverNameTemplate, "@idx", fmt.Sprintf("%.02d", nodeNumber), 1) serverOpts.Datacenter = &hcloud.Datacenter{ @@ -264,7 +260,7 @@ func (provider *Provider) runCreateServer(opts *hcloud.ServerCreateOpts) (*hclou } func (provider *Provider) actionProgress(action *hcloud.Action) error { - errCh, progressCh := waitAction(provider.context, provider.client, action) + progressCh, errCh := provider.client.Action.WatchProgress(provider.context, action) if term.IsTerminal(os.Stdout) { progress := uiprogress.New() diff --git a/pkg/hetzner/hetzner_provider_test.go b/pkg/hetzner/hetzner_provider_test.go index 1bfe6699..63efff66 100644 --- a/pkg/hetzner/hetzner_provider_test.go +++ b/pkg/hetzner/hetzner_provider_test.go @@ -1,8 +1,10 @@ package hetzner_test import ( + "context" "testing" + "github.com/hetznercloud/hcloud-go/hcloud" "github.com/magiconair/properties/assert" "github.com/xetys/hetzner-kube/pkg/clustermanager" "github.com/xetys/hetzner-kube/pkg/hetzner" @@ -297,7 +299,7 @@ func TestProviderGetMasterNodeIsMissing(t *testing.T) { _, err := provider.GetMasterNode() if err == nil { - t.Error("no error ommited with no master") + t.Error("no error omitted with no master") } }) } @@ -306,10 +308,10 @@ func TestProviderGetMasterNodeIsMissing(t *testing.T) { func TestProviderInitCluster(t *testing.T) { provider := getProviderWithNodes([]clustermanager.Node{}) - provider.InitCluster("cluster-name", "10.0.1.0/24") + _, err := provider.GetMasterNode() - if provider.GetNodeCidr() != "10.0.1.0/24" { - t.Error("cluster node cidr is not correctly set") + if err == nil { + t.Error("no error omitted with no master") } } @@ -321,10 +323,19 @@ func TestProviderGetCluster(t *testing.T) { {Name: "kube-master-1", IsMaster: true}, {Name: "kube-worker-1"}, } - provider := getProviderWithNodes(nodes) - provider.InitCluster("cluster-name", "10.0.1.0/24") - provider.SetCloudInitFile("cloud/init.file") + provider := hetzner.NewHetznerProvider( + context.Background(), + &hcloud.Client{}, + clustermanager.Cluster{ + Name: "cluster-name", + NodeCIDR: "10.0.1.0/24", + CloudInitFile: "cloud/init.file", + }, + "token-string", + ) + + provider.SetNodes(nodes) cluster := provider.GetCluster() expectedCluster := clustermanager.Cluster{ diff --git a/pkg/hetzner/util.go b/pkg/hetzner/util.go deleted file mode 100644 index 6d10045a..00000000 --- a/pkg/hetzner/util.go +++ /dev/null @@ -1,71 +0,0 @@ -package hetzner - -import ( - "context" - "time" - - "github.com/hetznercloud/hcloud-go/hcloud" - "github.com/xetys/hetzner-kube/pkg/clustermanager" -) - -//ProviderAndManager get the provider and the manager for the cluster -func ProviderAndManager(context context.Context, cluster clustermanager.Cluster, client *hcloud.Client, nc clustermanager.NodeCommunicator, eventService clustermanager.EventService, token string) (*Provider, *clustermanager.Manager) { - provider := NewHetznerProvider(context, client, token) - provider.InitCluster(cluster.Name, cluster.NodeCIDR) - provider.SetNodes(cluster.Nodes) - manager := clustermanager.NewClusterManagerFromCluster(cluster, provider, nc, eventService) - - return provider, manager -} - -func waitAction(ctx context.Context, client *hcloud.Client, action *hcloud.Action) (<-chan error, <-chan int) { - errCh := make(chan error, 1) - progressCh := make(chan int) - - go func() { - defer close(errCh) - defer close(progressCh) - - ticker := time.NewTicker(100 * time.Millisecond) - - sendProgress := func(p int) { - select { - case progressCh <- p: - break - default: - break - } - } - - for { - select { - case <-ctx.Done(): - errCh <- ctx.Err() - return - case <-ticker.C: - break - } - - action, _, err := client.Action.GetByID(ctx, action.ID) - if err != nil { - errCh <- ctx.Err() - return - } - - switch action.Status { - case hcloud.ActionStatusRunning: - sendProgress(action.Progress) - break - case hcloud.ActionStatusSuccess: - sendProgress(100) - errCh <- nil - return - case hcloud.ActionStatusError: - errCh <- action.Error() - return - } - } - }() - - return errCh, progressCh -}