sigs.k8s.io/kubebuilder/v3@v3.14.0/pkg/plugins/golang/go_version.go (about) 1 /* 2 Copyright 2018 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package golang 18 19 import ( 20 "fmt" 21 "os/exec" 22 "regexp" 23 "strconv" 24 "strings" 25 ) 26 27 const ( 28 goVerPattern = `^go(?P<major>[0-9]+)\.(?P<minor>[0-9]+)(?:\.(?P<patch>[0-9]+)|(?P<pre>(?:alpha|beta|rc)[0-9]+))?$` 29 ) 30 31 var ( 32 goVerRegexp = regexp.MustCompile(goVerPattern) 33 ) 34 35 // GoVersion describes a Go version. 36 type GoVersion struct { 37 major, minor, patch int 38 prerelease string 39 } 40 41 func (v GoVersion) String() string { 42 switch { 43 case v.patch != 0: 44 return fmt.Sprintf("go%d.%d.%d", v.major, v.minor, v.patch) 45 case v.prerelease != "": 46 return fmt.Sprintf("go%d.%d%s", v.major, v.minor, v.prerelease) 47 } 48 return fmt.Sprintf("go%d.%d", v.major, v.minor) 49 } 50 51 // MustParse will panic if verStr does not match the expected Go version string spec. 52 func MustParse(verStr string) (v GoVersion) { 53 if err := v.parse(verStr); err != nil { 54 panic(err) 55 } 56 return v 57 } 58 59 func (v *GoVersion) parse(verStr string) error { 60 m := goVerRegexp.FindStringSubmatch(verStr) 61 if m == nil { 62 return fmt.Errorf("invalid version string") 63 } 64 65 var err error 66 67 v.major, err = strconv.Atoi(m[1]) 68 if err != nil { 69 return fmt.Errorf("error parsing major version '%s': %s", m[1], err) 70 } 71 72 v.minor, err = strconv.Atoi(m[2]) 73 if err != nil { 74 return fmt.Errorf("error parsing minor version '%s': %s", m[2], err) 75 } 76 77 if m[3] != "" { 78 v.patch, err = strconv.Atoi(m[3]) 79 if err != nil { 80 return fmt.Errorf("error parsing patch version '%s': %s", m[2], err) 81 } 82 } 83 84 v.prerelease = m[4] 85 86 return nil 87 } 88 89 // Compare returns -1, 0, or 1 if v < other, v == other, or v > other, respectively. 90 func (v GoVersion) Compare(other GoVersion) int { 91 if v.major > other.major { 92 return 1 93 } 94 if v.major < other.major { 95 return -1 96 } 97 98 if v.minor > other.minor { 99 return 1 100 } 101 if v.minor < other.minor { 102 return -1 103 } 104 105 if v.patch > other.patch { 106 return 1 107 } 108 if v.patch < other.patch { 109 return -1 110 } 111 112 if v.prerelease == other.prerelease { 113 return 0 114 } 115 if v.prerelease == "" { 116 return 1 117 } 118 if other.prerelease == "" { 119 return -1 120 } 121 if v.prerelease > other.prerelease { 122 return 1 123 } 124 return -1 125 } 126 127 // ValidateGoVersion verifies that Go is installed and the current go version is supported by a plugin. 128 func ValidateGoVersion(min, max GoVersion) error { 129 err := fetchAndCheckGoVersion(min, max) 130 if err != nil { 131 return fmt.Errorf("%s. You can skip this check using the --skip-go-version-check flag", err) 132 } 133 return nil 134 } 135 136 func fetchAndCheckGoVersion(min, max GoVersion) error { 137 cmd := exec.Command("go", "version") 138 out, err := cmd.Output() 139 if err != nil { 140 return fmt.Errorf("failed to retrieve 'go version': %v", string(out)) 141 } 142 143 split := strings.Split(string(out), " ") 144 if len(split) < 3 { 145 return fmt.Errorf("found invalid Go version: %q", string(out)) 146 } 147 goVer := split[2] 148 if err := checkGoVersion(goVer, min, max); err != nil { 149 return fmt.Errorf("go version '%s' is incompatible because '%s'", goVer, err) 150 } 151 return nil 152 } 153 154 func checkGoVersion(verStr string, min, max GoVersion) error { 155 var version GoVersion 156 if err := version.parse(verStr); err != nil { 157 return err 158 } 159 160 if version.Compare(min) < 0 || version.Compare(max) >= 0 { 161 return fmt.Errorf("plugin requires %s <= version < %s", min, max) 162 } 163 164 return nil 165 }