github.com/cilium/cilium@v1.16.2/tools/dev-doctor/rootcmd.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package main 5 6 import ( 7 "fmt" 8 "os" 9 "path/filepath" 10 "regexp" 11 "text/tabwriter" 12 13 "github.com/blang/semver/v4" 14 "github.com/spf13/cobra" 15 "golang.org/x/mod/modfile" 16 ) 17 18 var rootCmd = &cobra.Command{ 19 Args: cobra.NoArgs, 20 Short: "Check development setup", 21 Run: rootCmdRun, 22 } 23 24 var ( 25 backportingChecks *bool 26 nfsFirewallChecks *bool 27 ) 28 29 // versionRegex determines the semantic version via regexp. 30 const versionRegex string = `v?(\d+\.\d+(\.\d+)?\S*)` 31 32 func init() { 33 flags := rootCmd.Flags() 34 backportingChecks = flags.Bool("backporting", false, "Run backporting checks") 35 nfsFirewallChecks = flags.Bool("nfs-firewall", false, "Run extra NFS firewall checks, requires root privileges") 36 } 37 38 func readGoModGoVersion(rootDir string) (*semver.Version, error) { 39 goModFile := "go.mod" 40 path := filepath.Join(rootDir, goModFile) 41 data, err := os.ReadFile(path) 42 if err != nil { 43 return nil, err 44 } 45 mod, err := modfile.Parse(goModFile, data, nil) 46 if err != nil { 47 return nil, err 48 } 49 50 if mod.Go == nil { 51 return nil, fmt.Errorf("no go statement found in %s", path) 52 } 53 ver, err := semver.ParseTolerant(mod.Go.Version) 54 if err != nil { 55 return nil, err 56 } 57 return &ver, nil 58 } 59 60 func rootCmdRun(cmd *cobra.Command, args []string) { 61 rootDir := goPath() + "/src/github.com/cilium/cilium" 62 63 // $GOPATH is optional to set with a module-based Go setup 64 // If we cannot find src path via `$GOPATH`, just look in 65 // the `make` dir for `go.mod` 66 if _, err := os.Stat(rootDir); os.IsNotExist(err) { 67 rootDir, _ = os.Getwd() 68 } 69 70 minGoVersion, err := readGoModGoVersion(rootDir) 71 if err != nil { 72 panic(fmt.Sprintf("cannot read go version from go.mod: %v", err)) 73 } 74 75 checks := []check{ 76 osArchCheck{}, 77 unameCheck{}, 78 rootDirCheck{ 79 rootDir: rootDir, 80 }, 81 &binaryCheck{ 82 name: "make", 83 ifNotFound: checkError, 84 versionArgs: []string{"--version"}, 85 versionRegexp: regexp.MustCompile(`GNU\s+Make\s+` + versionRegex), 86 }, 87 &binaryCheck{ 88 name: "go", 89 ifNotFound: checkError, 90 versionArgs: []string{"version"}, 91 versionRegexp: regexp.MustCompile(`go version go` + versionRegex), 92 minVersion: minGoVersion, 93 }, 94 &binaryCheck{ 95 name: "tparse", 96 ifNotFound: checkWarning, 97 versionArgs: []string{"-v"}, 98 versionRegexp: regexp.MustCompile(`tparse version: ` + versionRegex), 99 hint: `Run "go install github.com/mfridman/tparse@latest"`, 100 }, 101 &binaryCheck{ 102 name: "clang", 103 ifNotFound: checkError, 104 versionArgs: []string{"--version"}, 105 versionRegexp: regexp.MustCompile(`clang version ` + versionRegex), 106 minVersion: &semver.Version{Major: 17, Minor: 0, Patch: 0}, 107 }, 108 &binaryCheck{ 109 name: "docker-server", 110 command: "docker", 111 ifNotFound: checkWarning, 112 versionArgs: []string{"version", "--format", "{{ .Server.Version }}"}, 113 versionRegexp: regexp.MustCompile(versionRegex), 114 }, 115 &binaryCheck{ 116 name: "docker-client", 117 command: "docker", 118 ifNotFound: checkWarning, 119 versionArgs: []string{"version", "--format", "{{ .Client.Version }}"}, 120 versionRegexp: regexp.MustCompile(versionRegex), 121 }, 122 &binaryCheck{ 123 name: "docker-buildx", 124 command: "docker", 125 ifNotFound: checkWarning, 126 versionArgs: []string{"buildx", "version"}, 127 versionRegexp: regexp.MustCompile(`github\.com/docker/buildx ` + versionRegex), 128 hint: "see https://docs.docker.com/buildx/working-with-buildx/", 129 }, 130 &binaryCheck{ 131 name: "ginkgo", 132 ifNotFound: checkWarning, 133 versionArgs: []string{"version"}, 134 versionRegexp: regexp.MustCompile(`Ginkgo Version ` + versionRegex), 135 minVersion: &semver.Version{Major: 1, Minor: 4, Patch: 0}, 136 maxVersion: &semver.Version{Major: 2, Minor: 0, Patch: 0}, 137 hint: `Run "go install github.com/onsi/ginkgo/ginkgo@v1.16.5".`, 138 }, 139 // FIXME add gomega check? 140 &binaryCheck{ 141 name: "golangci-lint", 142 ifNotFound: checkWarning, 143 versionArgs: []string{"version"}, 144 versionRegexp: regexp.MustCompile(versionRegex), 145 minVersion: &semver.Version{Major: 1, Minor: 27, Patch: 0}, 146 hint: "See https://golangci-lint.run/welcome/install/#local-installation.", 147 }, 148 &binaryCheck{ 149 name: "docker", 150 ifNotFound: checkError, 151 versionArgs: []string{"--version"}, 152 versionRegexp: regexp.MustCompile(`Docker version ` + versionRegex), 153 }, 154 &binaryCheck{ 155 name: "helm", 156 ifNotFound: checkWarning, 157 versionArgs: []string{`version`, `--template`, `Version: {{.Version}}`}, 158 versionRegexp: regexp.MustCompile(`Version: ` + versionRegex), 159 minVersion: &semver.Version{Major: 3, Minor: 13, Patch: 0}, 160 }, 161 &binaryCheck{ 162 name: "vagrant", 163 ifNotFound: checkInfo, 164 versionArgs: []string{"--version"}, 165 versionRegexp: regexp.MustCompile(`Vagrant ` + versionRegex), 166 minVersion: &semver.Version{Major: 2, Minor: 0, Patch: 0}, 167 }, 168 &binaryCheck{ 169 name: "virtualbox", 170 alternateNames: []string{"VirtualBox"}, 171 ifNotFound: checkInfo, 172 }, 173 &binaryCheck{ 174 name: "vboxheadless", 175 alternateNames: []string{"VBoxHeadless"}, 176 ifNotFound: checkInfo, 177 versionArgs: []string{"--version"}, 178 versionRegexp: regexp.MustCompile(`Oracle VM VirtualBox Headless Interface ` + versionRegex), 179 hint: "run \"VBoxHeadless --help\" to diagnose why vboxheadless failed to execute", 180 }, 181 &binaryCheck{ 182 name: "pip3", 183 ifNotFound: checkWarning, 184 versionArgs: []string{"--version"}, 185 versionRegexp: regexp.MustCompile(`pip ` + versionRegex), 186 }, 187 &binaryCheck{ 188 name: "kind", 189 ifNotFound: checkWarning, 190 versionArgs: []string{"--version"}, 191 versionRegexp: regexp.MustCompile(`kind version ` + versionRegex), 192 minVersion: &semver.Version{Major: 0, Minor: 7, Patch: 0}, 193 hint: "See https://kind.sigs.k8s.io/docs/user/quick-start/#installation.", 194 }, 195 &binaryCheck{ 196 name: "kubectl", 197 ifNotFound: checkWarning, 198 versionArgs: []string{"version", "--output=yaml", "--client=true"}, 199 versionRegexp: regexp.MustCompile(`gitVersion: ` + versionRegex), 200 minVersion: &semver.Version{Major: 1, Minor: 26, Patch: 0}, 201 hint: "See https://kubernetes.io/docs/tasks/tools/#kubectl.", 202 }, 203 &binaryCheck{ 204 name: "cilium", 205 ifNotFound: checkWarning, 206 versionArgs: []string{"version", "--client"}, 207 versionRegexp: regexp.MustCompile(`cilium-cli: [A-Za-z/-]*` + versionRegex), 208 hint: "See https://docs.cilium.io/en/stable/gettingstarted/k8s-install-default/#install-the-cilium-cli.", 209 }, 210 dockerGroupCheck{}, 211 } 212 213 if *backportingChecks { 214 checks = append(checks, 215 &binaryCheck{ 216 name: "jq", 217 ifNotFound: checkError, 218 }, 219 &binaryCheck{ 220 name: "python3", 221 ifNotFound: checkError, 222 versionArgs: []string{"--version"}, 223 versionRegexp: regexp.MustCompile(`Python\s+` + versionRegex), 224 minVersion: &semver.Version{Major: 3, Minor: 6, Patch: 0}, 225 }, 226 &commandCheck{ 227 name: "pygithub", 228 command: "python3", 229 args: []string{"-c", "from github import Github"}, 230 ifFailure: checkWarning, 231 ifSuccessMessage: "pygithub installed", 232 hint: `Run "pip3 install --user PyGithub".`, 233 }, 234 &binaryCheck{ 235 name: "gh", 236 ifNotFound: checkError, 237 versionArgs: []string{"--version"}, 238 versionRegexp: regexp.MustCompile(`gh\s+version\s+` + versionRegex), 239 minVersion: &semver.Version{Major: 2, Minor: 14, Patch: 0}, 240 hint: `Download the latest version from https://cli.github.com`, 241 }, 242 &envVarCheck{ 243 name: "GITHUB_TOKEN", 244 ifNotSetOrEmpty: checkInfo, 245 }, 246 ) 247 } 248 249 if *nfsFirewallChecks { 250 checks = append(checks, 251 etcNFSConfCheck{}, 252 &iptablesRuleCheck{ 253 rule: []string{"INPUT", "-p", "tcp", "-s", "192.168.61.0/24", "--dport", "111", "-j", "ACCEPT"}, 254 }, 255 &iptablesRuleCheck{ 256 rule: []string{"INPUT", "-p", "tcp", "-s", "192.168.61.0/24", "--dport", "2049", "-j", "ACCEPT"}, 257 }, 258 &iptablesRuleCheck{ 259 rule: []string{"INPUT", "-p", "tcp", "-s", "192.168.61.0/24", "--dport", "20048", "-j", "ACCEPT"}, 260 }, 261 ) 262 } 263 264 worstResult := checkOK 265 resultWriter := tabwriter.NewWriter(os.Stdout, 3, 0, 3, ' ', 0) 266 fmt.Fprint(resultWriter, "RESULT\tCHECK\tMESSAGE\n") 267 hints := make([]string, 0, len(checks)) 268 for _, check := range checks { 269 checkResult, message := check.Run() 270 fmt.Fprintf(resultWriter, "%s\t%s\t%s\n", checkResultStr[checkResult], check.Name(), message) 271 if checkResult > checkOK { 272 if hint := check.Hint(); hint != "" { 273 hints = append(hints, fmt.Sprintf("%s\t%s\n", check.Name(), hint)) 274 } 275 } 276 if checkResult > worstResult { 277 worstResult = checkResult 278 } 279 } 280 resultWriter.Flush() 281 282 if len(hints) > 0 { 283 fmt.Println() 284 hintWriter := tabwriter.NewWriter(os.Stdout, 3, 0, 3, ' ', 0) 285 fmt.Fprint(hintWriter, "CHECK\tHINT\n") 286 for _, hint := range hints { 287 fmt.Fprint(hintWriter, hint) 288 } 289 hintWriter.Flush() 290 } 291 292 if worstResult > checkOK { 293 fmt.Println() 294 fmt.Println("See https://docs.cilium.io/en/latest/contributing/development/dev_setup/.") 295 if *backportingChecks { 296 fmt.Println("See https://docs.cilium.io/en/latest/contributing/release/backports/.") 297 } 298 } 299 300 if worstResult > checkWarning { 301 os.Exit(1) 302 } 303 }