github.com/jlmeeker/kismatic@v1.10.1-0.20180612190640-57f9005a1f1a/pkg/install/add_node.go (about) 1 package install 2 3 import ( 4 "errors" 5 "fmt" 6 7 "github.com/apprenda/kismatic/pkg/util" 8 ) 9 10 var errMissingClusterCA = errors.New("The Certificate Authority's private key and certificate used to install " + 11 "the cluster are required for adding worker nodes.") 12 13 // AddNode adds a worker node to the original cluster described in the plan. 14 // If successful, the updated plan is returned. 15 func (ae *ansibleExecutor) AddNode(originalPlan *Plan, newNode Node, roles []string, restartServices bool) (*Plan, error) { 16 if err := checkAddNodePrereqs(ae.pki, newNode); err != nil { 17 return nil, err 18 } 19 updatedPlan := AddNodeToPlan(*originalPlan, newNode, roles) 20 21 // Generate node certificates 22 util.PrintHeader(ae.stdout, "Generating Certificate For New Node", '=') 23 ca, err := ae.pki.GetClusterCA() 24 if err != nil { 25 return nil, err 26 } 27 if err = ae.pki.GenerateNodeCertificate(&updatedPlan, newNode, ca); err != nil { 28 return nil, fmt.Errorf("error generating certificate for new node: %v", err) 29 } 30 31 // Run the playbook to add the node 32 inventory := buildInventoryFromPlan(&updatedPlan) 33 cc, err := ae.buildClusterCatalog(&updatedPlan) 34 if err != nil { 35 return nil, fmt.Errorf("failed to generate ansible vars: %v", err) 36 } 37 38 // We need to run ansible against all hosts to update the hosts files 39 if updatedPlan.Cluster.Networking.UpdateHostsFiles { 40 util.PrintHeader(ae.stdout, "Updating Hosts Files On All Nodes", '=') 41 t := task{ 42 name: "add-node-update-hosts", 43 playbook: "hosts.yaml", 44 plan: updatedPlan, 45 inventory: inventory, 46 clusterCatalog: *cc, 47 explainer: ae.defaultExplainer(), 48 } 49 if err = ae.execute(t); err != nil { 50 return nil, fmt.Errorf("error updating hosts files on all nodes: %v", err) 51 } 52 } 53 54 if restartServices { 55 cc.EnableRestart() 56 } 57 util.PrintHeader(ae.stdout, "Adding New Node to Cluster", '=') 58 t := task{ 59 name: "add-node", 60 playbook: "kubernetes-node.yaml", 61 plan: updatedPlan, 62 inventory: inventory, 63 clusterCatalog: *cc, 64 explainer: ae.defaultExplainer(), 65 limit: []string{newNode.Host}, 66 } 67 if err = ae.execute(t); err != nil { 68 return nil, fmt.Errorf("error running playbook: %v", err) 69 } 70 71 // Verify that the node registered with API server 72 util.PrintHeader(ae.stdout, "Running New Node Smoke Test", '=') 73 cc.NewNode = newNode.Host 74 t = task{ 75 name: "add-node-smoke-test", 76 playbook: "_node-smoke-test.yaml", 77 plan: updatedPlan, 78 inventory: inventory, 79 clusterCatalog: *cc, 80 explainer: ae.defaultExplainer(), 81 limit: []string{newNode.Host}, 82 } 83 if err = ae.execute(t); err != nil { 84 return nil, fmt.Errorf("error running node smoke test: %v", err) 85 } 86 87 // Allow access to new node to any storage volumes defined 88 if len(originalPlan.Storage.Nodes) > 0 { 89 util.PrintHeader(ae.stdout, "Updating Allowed IPs On Storage Volumes", '=') 90 t = task{ 91 name: "add-node-update-volumes", 92 playbook: "_volume-update-allowed.yaml", 93 plan: updatedPlan, 94 inventory: inventory, 95 clusterCatalog: *cc, 96 explainer: ae.defaultExplainer(), 97 } 98 if err = ae.execute(t); err != nil { 99 return nil, fmt.Errorf("error adding new node to volume allow list: %v", err) 100 } 101 } 102 return &updatedPlan, nil 103 } 104 105 func AddNodeToPlan(plan Plan, node Node, roles []string) Plan { 106 if util.Contains("worker", roles) { 107 plan.Worker.ExpectedCount++ 108 plan.Worker.Nodes = append(plan.Worker.Nodes, node) 109 } 110 if util.Contains("ingress", roles) { 111 plan.Ingress.ExpectedCount++ 112 plan.Ingress.Nodes = append(plan.Ingress.Nodes, node) 113 } 114 if util.Contains("storage", roles) { 115 plan.Storage.ExpectedCount++ 116 plan.Storage.Nodes = append(plan.Storage.Nodes, node) 117 } 118 119 return plan 120 } 121 122 // ensure the assumptions we are making are solid 123 func checkAddNodePrereqs(pki PKI, newNode Node) error { 124 // 1. if the node certificate is not there, we need to ensure that 125 // the CA is available for generating the new nodes's cert 126 // don't check for a valid cert here since its already being done in GenerateNodeCertificate() 127 certExists, err := pki.NodeCertificateExists(newNode) 128 if err != nil { 129 return fmt.Errorf("error while checking if node's certificate exists: %v", err) 130 } 131 if !certExists { 132 caExists, err := pki.CertificateAuthorityExists() 133 if err != nil { 134 return fmt.Errorf("error while checking if cluster CA exists: %v", err) 135 } 136 if !caExists { 137 return errMissingClusterCA 138 } 139 } 140 return nil 141 }