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  }