github.com/jlmeeker/kismatic@v1.10.1-0.20180612190640-57f9005a1f1a/pkg/install/about.go (about) 1 package install 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/apprenda/kismatic/pkg/ssh" 8 "github.com/apprenda/kismatic/pkg/util" 9 "github.com/blang/semver" 10 11 yaml "gopkg.in/yaml.v2" 12 ) 13 14 // ClusterVersion contains version information about the cluster 15 type ClusterVersion struct { 16 EarliestVersion semver.Version 17 LatestVersion semver.Version 18 IsTransitioning bool 19 Nodes []ListableNode 20 } 21 22 // ListableNode contains version and role information about a given node 23 type ListableNode struct { 24 Node Node 25 Roles []string 26 Version semver.Version 27 ComponentVersions ComponentVersions 28 } 29 30 type ComponentVersions struct { 31 Kubernetes string 32 } 33 34 // KismaticVersion contains the version information of the currently running binary 35 var KismaticVersion semver.Version 36 37 // SetVersion parses the given version, and sets it as the global version of the binary 38 func SetVersion(v string) { 39 ver, err := parseVersion(v) 40 if err != nil { 41 panic("failed to parse version " + v) 42 } 43 KismaticVersion = ver 44 } 45 46 // IsOlderVersion returns true if the provided version is older than the current Kismatic version 47 func IsOlderVersion(that semver.Version) bool { 48 this := KismaticVersion 49 return this.GT(that) 50 } 51 52 // IsLessThanVersion parses the version from a string and returns true if this version is less than that version 53 func IsLessThanVersion(this semver.Version, that string) bool { 54 thatVersion, err := parseVersion(that) 55 if err != nil { 56 panic("failed to parse version " + that) 57 } 58 59 return this.LT(thatVersion) 60 } 61 62 // ListVersions connects to the cluster described in the plan file and 63 // gathers version information about it. 64 func ListVersions(plan *Plan) (ClusterVersion, error) { 65 nodes := plan.GetUniqueNodes() 66 cv := ClusterVersion{ 67 Nodes: []ListableNode{}, 68 } 69 70 sshDeets := plan.Cluster.SSH 71 ketVerFile := "/etc/kismatic-version" 72 componentVerFile := "/etc/component-versions" 73 for i, node := range nodes { 74 client, err := ssh.NewClient(node.IP, sshDeets.Port, sshDeets.User, sshDeets.Key) 75 if err != nil { 76 return cv, fmt.Errorf("error creating SSH client: %v", err) 77 } 78 79 // get KET version 80 ketOutput, err := client.Output(false, fmt.Sprintf("cat %s", ketVerFile)) 81 if err != nil { 82 // the output var contains the actual error message from the cat command, which has 83 // more meaningful info 84 return cv, fmt.Errorf("error getting KET version for node %q: %q", node.Host, ketOutput) 85 } 86 87 thisVersion, err := parseVersion(ketOutput) 88 if err != nil { 89 return cv, fmt.Errorf("invalid version %q found in version file %q of node %s", ketOutput, ketVerFile, node.Host) 90 } 91 92 // get component versions 93 versionsOutput, err := client.Output(false, fmt.Sprintf("cat %s", componentVerFile)) 94 // don't fail if the file is not found, will default to empty 95 // TODO remove 96 if err != nil && !strings.Contains(versionsOutput, "No such file or directory") { 97 // the output var contains the actual error message from the cat command, which has 98 // more meaningful info 99 return cv, fmt.Errorf("error getting component versions for node %q: %q", node.Host, versionsOutput) 100 } 101 versions := ComponentVersions{} 102 if !strings.Contains(versionsOutput, "No such file or directory") { 103 err = yaml.Unmarshal([]byte(versionsOutput), &versions) 104 if err != nil { 105 return cv, fmt.Errorf("error unmarshalling component versions file: %q", componentVerFile) 106 } 107 } 108 109 cv.Nodes = append(cv.Nodes, ListableNode{node, plan.GetRolesForIP(node.IP), thisVersion, versions}) 110 111 // If looking at the first node, set the versions and move on 112 if i == 0 { 113 cv.EarliestVersion = thisVersion 114 cv.LatestVersion = thisVersion 115 continue 116 } 117 118 if thisVersion.GT(cv.LatestVersion) { 119 cv.LatestVersion = thisVersion 120 } 121 if cv.EarliestVersion.GT(thisVersion) { 122 cv.EarliestVersion = thisVersion 123 } 124 } 125 126 cv.IsTransitioning = cv.EarliestVersion.NE(cv.LatestVersion) 127 return cv, nil 128 } 129 130 // NodesWithRoles returns a filtered list of ListableNode slice based on the node's roles 131 func NodesWithRoles(nodes []ListableNode, roles ...string) []ListableNode { 132 var subset []ListableNode 133 for _, need := range roles { 134 for _, n := range nodes { 135 if util.Subset([]string{need}, n.Roles) { 136 subset = append(subset, n) 137 } 138 } 139 } 140 return subset 141 }