pax_global_header00006660000000000000000000000064140111644700014510gustar00rootroot0000000000000052 comment=ef4e32f5ec74736d0b7f89bb1c1a75999a344267 system-validators-1.4.0/000077500000000000000000000000001401116447000152045ustar00rootroot00000000000000system-validators-1.4.0/CONTRIBUTING.md000066400000000000000000000032451401116447000174410ustar00rootroot00000000000000# Contributing Guidelines Welcome to Kubernetes. We are excited about the prospect of you joining our [community](https://git.k8s.io/community)! The Kubernetes community abides by the CNCF [code of conduct](code-of-conduct.md). Here is an excerpt: _As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities._ ## Getting Started We have full documentation on how to get started contributing here: - [Contributor License Agreement](https://git.k8s.io/community/CLA.md) Kubernetes projects require that you sign a Contributor License Agreement (CLA) before we can accept your pull requests - [Kubernetes Contributor Guide](https://git.k8s.io/community/contributors/guide) - Main contributor documentation, or you can just jump directly to the [contributing section](https://git.k8s.io/community/contributors/guide#contributing) - [Contributor Cheat Sheet](https://git.k8s.io/community/contributors/guide/contributor-cheatsheet/README.md) - Common resources for existing developers ## Mentorship - [Mentoring Initiatives](https://git.k8s.io/community/mentoring) - We have a diverse set of mentorship programs available that are always looking for volunteers! ## Contact Information - [Slack](https://kubernetes.slack.com/messages/sig-cluster-lifecycle) - [Mailing List](https://groups.google.com/forum/#!forum/kubernetes-sig-cluster-lifecycle) system-validators-1.4.0/LICENSE000066400000000000000000000261351401116447000162200ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. system-validators-1.4.0/OWNERS000066400000000000000000000003561401116447000161500ustar00rootroot00000000000000# See the OWNERS docs at https://go.k8s.io/owners approvers: - fabriziopandini - neolit123 - SataQiu reviewers: - KentaTada - odinuge emeritus_approvers: - luxas - timothysc - rosti - ereslibre labels: - sig/cluster-lifecycle - sig/node system-validators-1.4.0/README.md000066400000000000000000000027621401116447000164720ustar00rootroot00000000000000# System Validators A set of system-oriented validators for kubeadm preflight checks. ## Creating releases To prepare a release of this library please follow this guide: - The main branch should always contain WIP commits planned for the upcoming release. - Always create a new branch for MAJOR and MINOR releases. This allows backporting changes. - Release branch names should be in the format `release-MAJOR.MINOR` (without a `v` prefix). - Only non-breaking bug fixes can be done in a PATCH release. - New features must not be added in PATCH releases. - Breaking changes must be added in a MAJOR release. - Pushing releases requires write access. To obtain that you must be part of the [`system-validator-maintainers` team](http://git.k8s.io/org/config/kubernetes/sig-cluster-lifecycle/teams.yaml). For vendoring the new release in kubernetes/kubernetes you can use its `pin-dependency.sh` script. Example: ```bash ./hack/pin-dependency.sh k8s.io/system-validators ``` And then PR the changes. ## Community, discussion, contribution, and support Learn how to engage with the Kubernetes community on the [community page](http://kubernetes.io/community/). You can reach the maintainers of this project at: - [Slack](https://kubernetes.slack.com/messages/sig-cluster-lifecycle) - [Mailing List](https://groups.google.com/forum/#!forum/kubernetes-sig-cluster-lifecycle) ### Code of conduct Participation in the Kubernetes community is governed by the [Kubernetes Code of Conduct](code-of-conduct.md). system-validators-1.4.0/RELEASE.md000066400000000000000000000007751401116447000166170ustar00rootroot00000000000000# Release Process The system-validators is released on an as-needed basis. The process is as follows: 1. An issue is proposing a new release with a changelog since the last release 1. All [OWNERS](OWNERS) must LGTM this release 1. An OWNER runs `git tag -s $VERSION` and inserts the changelog and pushes the tag with `git push $VERSION` 1. The release issue is closed 1. An announcement email is sent to `kubernetes-dev@googlegroups.com` with the subject `[ANNOUNCE] system-validators $VERSION is released` system-validators-1.4.0/SECURITY.md000066400000000000000000000020551401116447000167770ustar00rootroot00000000000000# Security Policy ## Security Announcements Join the [kubernetes-security-announce] group for security and vulnerability announcements. You can also subscribe to an RSS feed of the above using [this link][kubernetes-security-announce-rss]. ## Reporting a Vulnerability Instructions for reporting a vulnerability can be found on the [Kubernetes Security and Disclosure Information] page. ## Supported Versions Information about supported Kubernetes versions can be found on the [Kubernetes version and version skew support policy] page on the Kubernetes website. [kubernetes-security-announce]: https://groups.google.com/forum/#!forum/kubernetes-security-announce [kubernetes-security-announce-rss]: https://groups.google.com/forum/feed/kubernetes-security-announce/msgs/rss_v2_0.xml?num=50 [Kubernetes version and version skew support policy]: https://kubernetes.io/docs/setup/release/version-skew-policy/#supported-versions [Kubernetes Security and Disclosure Information]: https://kubernetes.io/docs/reference/issues-security/security/#report-a-vulnerability system-validators-1.4.0/SECURITY_CONTACTS000066400000000000000000000010771401116447000177010ustar00rootroot00000000000000# Defined below are the security contacts for this repo. # # They are the contact point for the Product Security Committee to reach out # to for triaging and handling of incoming issues. # # The below names agree to abide by the # [Embargo Policy](https://git.k8s.io/security/private-distributors-list.md#embargo-policy) # and will be removed and replaced if they violate that agreement. # # DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE # INSTRUCTIONS AT https://kubernetes.io/security/ cjcullen joelsmith jonpulsifer liggitt philips tallclair system-validators-1.4.0/code-of-conduct.md000066400000000000000000000002241401116447000204750ustar00rootroot00000000000000# Kubernetes Community Code of Conduct Please refer to our [Kubernetes Community Code of Conduct](https://git.k8s.io/community/code-of-conduct.md) system-validators-1.4.0/go.mod000066400000000000000000000003321401116447000163100ustar00rootroot00000000000000module k8s.io/system-validators go 1.13 require ( github.com/blang/semver v3.5.0+incompatible github.com/pkg/errors v0.8.0 github.com/stretchr/testify v1.3.0 golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7 ) system-validators-1.4.0/go.sum000066400000000000000000000022171401116447000163410ustar00rootroot00000000000000github.com/blang/semver v3.5.0+incompatible h1:CGxCgetQ64DKk7rdZ++Vfnb1+ogGNnB17OJKJXD2Cfs= github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7 h1:HmbHVPwrPEKPGLAcHSrMe6+hqSUlvZU0rab6x5EXfGU= golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= system-validators-1.4.0/hack/000077500000000000000000000000001401116447000161125ustar00rootroot00000000000000system-validators-1.4.0/hack/update-all.sh000077500000000000000000000015431401116447000205040ustar00rootroot00000000000000#!/usr/bin/env bash # Copyright 2018 The Kubernetes Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # script to run all update scripts (except deps) set -o errexit set -o nounset set -o pipefail # shellcheck source=/dev/null source "$(dirname "$0")/utils.sh" REPO_PATH=$(get_root_path) "${REPO_PATH}"/hack/update-deps.sh "${REPO_PATH}"/hack/update-gofmt.sh system-validators-1.4.0/hack/update-deps.sh000077500000000000000000000015111401116447000206620ustar00rootroot00000000000000#!/usr/bin/env bash # Copyright 2018 The Kubernetes Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Runs go mod tidy # # Usage: # update-deps.sh set -o nounset set -o errexit set -o pipefail # shellcheck source=/dev/null source "$(dirname "$0")/utils.sh" # cd to the root path cd_root_path export GO111MODULE="on" go mod tidy system-validators-1.4.0/hack/update-gofmt.sh000077500000000000000000000015011401116447000210420ustar00rootroot00000000000000#!/usr/bin/env bash # Copyright 2019 The Kubernetes Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # script to run gofmt over our code (not vendor) set -o errexit set -o nounset set -o pipefail # shellcheck source=/dev/null source "$(dirname "$0")/utils.sh" # cd to the root path cd_root_path # update go fmt go fmt ./... system-validators-1.4.0/hack/utils.sh000066400000000000000000000015241401116447000176100ustar00rootroot00000000000000#!/usr/bin/env bash # Copyright 2019 The Kubernetes Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # get_root_path returns the root path of the kinder source tree get_root_path() { echo "$(git rev-parse --show-toplevel)" } # cd_root_path cds to the root path of the kinder source tree cd_root_path() { cd "$(get_root_path)" || exit } system-validators-1.4.0/hack/verify-all.sh000077500000000000000000000043111401116447000205220ustar00rootroot00000000000000#!/usr/bin/env bash # Copyright 2019 The Kubernetes Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set -o errexit set -o nounset set -o pipefail # shellcheck source=/dev/null source "$(dirname "$0")/utils.sh" # set REPO_PATH REPO_PATH=$(get_root_path) cd "${REPO_PATH}" # exit code, if a script fails we'll set this to 1 res=0 # run all verify scripts, optionally skipping any of them if [[ "${VERIFY_WHITESPACE:-true}" == "true" ]]; then echo "[*] Verifying whitespace..." hack/verify-whitespace.sh || res=1 cd "${REPO_PATH}" fi if [[ "${VERIFY_SPELLING:-true}" == "true" ]]; then echo "[*] Verifying spelling..." hack/verify-spelling.sh || res=1 cd "${REPO_PATH}" fi if [[ "${VERIFY_BOILERPLATE:-true}" == "true" ]]; then echo "[*] Verifying boilerplate..." hack/verify-boilerplate.sh || res=1 cd "${REPO_PATH}" fi if [[ "${VERIFY_GOFMT:-true}" == "true" ]]; then echo "[*] Verifying gofmt..." hack/verify-gofmt.sh || res=1 cd "${REPO_PATH}" fi if [[ "${VERIFY_GOLINT:-true}" == "true" ]]; then echo "[*] Verifying golint..." hack/verify-golint.sh || res=1 cd "${REPO_PATH}" fi if [[ "${VERIFY_GOVET:-true}" == "true" ]]; then echo "[*] Verifying govet..." hack/verify-govet.sh || res=1 cd "${REPO_PATH}" fi if [[ "${VERIFY_DEPS:-true}" == "true" ]]; then echo "[*] Verifying deps..." hack/verify-deps.sh || res=1 cd "${REPO_PATH}" fi if [[ "${VERIFY_GOTEST:-true}" == "true" ]]; then echo "[*] Verifying gotest..." hack/verify-gotest.sh || res=1 cd "${REPO_PATH}" fi # exit based on verify scripts if [[ "${res}" = 0 ]]; then echo "" echo "All verify checks passed, congrats!" else echo "" echo "One or more verify checks failed! See output above..." fi exit "${res}" system-validators-1.4.0/hack/verify-boilerplate.go000066400000000000000000000107571401116447000222570ustar00rootroot00000000000000/* Copyright 2019 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package main import ( "errors" "fmt" "io/ioutil" "os" "regexp" "strings" ) const ( yearPlaceholder = "YEAR" boilerPlateStart = "Copyright " boilerPlateEnd = "limitations under the License." ) var ( supportedExt = []string{".go", ".py", ".sh"} yearRegexp = regexp.MustCompile("(20)[0-9][0-9]") boilerPlate = []string{ boilerPlateStart + yearPlaceholder + " The Kubernetes Authors.", "", `Licensed under the Apache License, Version 2.0 (the "License");`, "you may not use this file except in compliance with the License.", "You may obtain a copy of the License at", "", " http://www.apache.org/licenses/LICENSE-2.0", "", "Unless required by applicable law or agreed to in writing, software", `distributed under the License is distributed on an "AS IS" BASIS,`, "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", "See the License for the specific language governing permissions and", boilerPlateEnd, } ) // trimLeadingComment strips a single line comment characters such as # or // // at the exact beginning of a line, but also the first possible space character after it. func trimLeadingComment(line, c string) string { if strings.Index(line, c) == 0 { x := len(c) if len(line) == x { return "" } if line[x] == byte(' ') { return line[x+1:] } return line[x:] } return line } // verifyFileExtension verifies if the file extensions is supported func isSupportedFileExtension(filePath string) bool { // check if the file has an extension idx := strings.LastIndex(filePath, ".") if idx == -1 { return false } // check if the file has a supported extension ext := filePath[idx : idx+len(filePath)-idx] for _, e := range supportedExt { if e == ext { return true } } return false } // verifyBoilerplate verifies if a string contains the boilerplate func verifyBoilerplate(contents string) error { idx := 0 foundBoilerplateStart := false lines := strings.Split(contents, "\n") for _, line := range lines { // handle leading comments line = trimLeadingComment(line, "//") line = trimLeadingComment(line, "#") // find the start of the boilerplate bpLine := boilerPlate[idx] if strings.Contains(line, boilerPlateStart) { foundBoilerplateStart = true // validate the year of the copyright yearWords := strings.Split(line, " ") expectedLen := len(strings.Split(boilerPlate[0], " ")) if len(yearWords) != expectedLen { return fmt.Errorf("copyright line should contain exactly %d words", expectedLen) } if !yearRegexp.MatchString(yearWords[1]) { return fmt.Errorf("cannot parse the year in the copyright line") } bpLine = strings.ReplaceAll(bpLine, yearPlaceholder, yearWords[1]) } // match line by line if foundBoilerplateStart { if line != bpLine { return fmt.Errorf("boilerplate line %d does not match\nexpected: %q\ngot: %q", idx+1, bpLine, line) } idx++ // exit after the last line is found if strings.Index(line, boilerPlateEnd) == 0 { break } } } if !foundBoilerplateStart { return errors.New("the file is missing a boilerplate") } if idx < len(boilerPlate) { return errors.New("boilerplate has missing lines") } return nil } // verifyFile verifies if a file contains the boilerplate func verifyFile(filePath string) error { if len(filePath) == 0 { return errors.New("empty file name") } if !isSupportedFileExtension(filePath) { fmt.Printf("skipping %q: unsupported file type\n", filePath) return nil } // read the file b, err := ioutil.ReadFile(filePath) if err != nil { return err } return verifyBoilerplate(string(b)) } func main() { if len(os.Args) < 2 { fmt.Println("usage: " + "go run verify-boilerplate.go ...") os.Exit(1) } hasErr := false for _, filePath := range os.Args[1:] { if err := verifyFile(filePath); err != nil { fmt.Printf("error validating %q: %v\n", filePath, err) hasErr = true } } if hasErr { os.Exit(1) } } system-validators-1.4.0/hack/verify-boilerplate.sh000077500000000000000000000015011401116447000222520ustar00rootroot00000000000000#!/usr/bin/env bash # Copyright 2019 The Kubernetes Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set -o errexit set -o nounset set -o pipefail # shellcheck source=/dev/null source "$(dirname "$0")/utils.sh" # cd to the root path cd_root_path git ls-files | grep -v "vendor\/" | xargs go run ./hack/verify-boilerplate.go system-validators-1.4.0/hack/verify-boilerplate_test.go000066400000000000000000000055031401116447000233070ustar00rootroot00000000000000/* Copyright 2019 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package main import ( "testing" ) func TestVerifyBoilerPlate(t *testing.T) { testcases := []struct { name string bp string expectedError bool }{ { name: "valid: boilerplate is valid", bp: `\/* Copyright 2019 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */`, expectedError: false, }, { name: "invalid: missing lines", bp: ` Copyright 2019 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); `, expectedError: true, }, { name: "invalid: bad year", bp: "Copyright 1019 The Kubernetes Authors.", expectedError: true, }, } for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { if err := verifyBoilerplate(tc.bp); err != nil != tc.expectedError { t.Errorf("expected error: %v, got: %v, error: %v", tc.expectedError, err != nil, err) } }) } } func TestTrimLeadingComment(t *testing.T) { testcases := []struct { name string comment string line string expectedResult string }{ { name: "trim leading comment", comment: "#", line: "# test", expectedResult: "test", }, { name: "empty line", comment: "#", line: "#", expectedResult: "", }, { name: "trim leading comment and space", comment: "//", line: "// test", expectedResult: "test", }, { name: "no comment", comment: "//", line: "test", expectedResult: "test", }, } for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { if res := trimLeadingComment(tc.line, tc.comment); res != tc.expectedResult { t.Errorf("expected: %q, got: %q", tc.expectedResult, res) } }) } } system-validators-1.4.0/hack/verify-deps.sh000077500000000000000000000024351401116447000207120ustar00rootroot00000000000000#!/usr/bin/env bash # Copyright 2019 The Kubernetes Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set -o nounset set -o pipefail # shellcheck source=/dev/null source "$(dirname "$0")/utils.sh" # cd to the root path cd_root_path # cleanup on exit cleanup() { echo "Cleaning up..." mv go.mod.old go.mod mv go.sum.old go.sum } trap cleanup EXIT echo "Verifying..." # temporary copy the go mod and sum files cp go.mod go.mod.old || exit cp go.sum go.sum.old || exit # run update-deps.sh export GO111MODULE="on" ./hack/update-deps.sh # compare the old and new files DIFF0=$(diff -u go.mod go.mod.old) DIFF1=$(diff -u go.sum go.sum.old) if [[ -n "${DIFF0}" ]] || [[ -n "${DIFF1}" ]]; then echo "${DIFF0}" echo "${DIFF1}" echo "Check failed. Please run ./hack/update-deps.sh" exit 1 fi system-validators-1.4.0/hack/verify-gofmt.sh000077500000000000000000000017231401116447000210720ustar00rootroot00000000000000#!/usr/bin/env bash # Copyright 2019 The Kubernetes Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set -o errexit set -o nounset set -o pipefail # shellcheck source=/dev/null source "$(dirname "$0")/utils.sh" # cd to the root path cd_root_path # check for gofmt diffs diff=$(git ls-files | grep "\.go" | grep -v "vendor\/" | xargs gofmt -s -d 2>&1) if [[ -n "${diff}" ]]; then echo "${diff}" echo echo "Check failed. Please run hack/update-gofmt.sh" exit 1 fi system-validators-1.4.0/hack/verify-golint.sh000077500000000000000000000025611401116447000212530ustar00rootroot00000000000000#!/usr/bin/env bash # Copyright 2019 The Kubernetes Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # CI script to run go lint over our code set -o errexit set -o nounset set -o pipefail # shellcheck source=/dev/null source "$(dirname "$0")/utils.sh" # cd to the root path REPO_PATH=$(get_root_path) # create a temporary directory TMP_DIR=$(mktemp -d) # cleanup exitHandler() ( echo "Cleaning up..." rm -rf "${TMP_DIR}" ) trap exitHandler EXIT # pull the source code and build the binary cd "${TMP_DIR}" URL="http://github.com/golang/lint" echo "Cloning ${URL} in ${TMP_DIR}..." git clone --quiet --depth=1 "${URL}" . echo "Building golint..." export GO111MODULE=on go build -o ./golint/golint ./golint # run the binary cd "${REPO_PATH}" echo "Running golint..." git ls-files | grep "\.go" | \ grep -v "vendor\/" | \ xargs -L1 "${TMP_DIR}/golint/golint" -set_exit_status system-validators-1.4.0/hack/verify-gotest.sh000077500000000000000000000020561401116447000212630ustar00rootroot00000000000000#!/usr/bin/env bash # Copyright 2019 The Kubernetes Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set -o errexit set -o nounset set -o pipefail # shellcheck source=/dev/null source "$(dirname "$0")/utils.sh" # cd to the root path cd_root_path # run go test export GO111MODULE=on ERROR=0 DIRS=$(git ls-files | grep -v "vendor\/" | grep ".go" | xargs dirname | grep -v "\." | cut -d '/' -f 1 | uniq) while read -r dir; do go test ./"$dir"/... || ERROR=1 done <<< "$DIRS" if [[ "${ERROR}" = 1 ]]; then echo "found errors in go test!" fi exit ${ERROR} system-validators-1.4.0/hack/verify-govet.sh000077500000000000000000000022221401116447000210750ustar00rootroot00000000000000#!/usr/bin/env bash # Copyright 2019 The Kubernetes Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # CI script to run go vet over our code set -o errexit set -o nounset set -o pipefail # shellcheck source=/dev/null source "$(dirname "$0")/utils.sh" # cd to the root path cd_root_path # run go vet export GO111MODULE=on ERROR=0 DIRS=$(git ls-files | grep -v "vendor\/" | grep ".go" | xargs dirname | grep -v "\." | cut -d '/' -f 1 | uniq) while read -r dir; do for os in {darwin,linux,windows}; do GOOS=$os go vet ./"$dir"/... || ERROR=1 done done <<< "$DIRS" if [[ "${ERROR}" = 1 ]]; then echo "found errors in go vet!" fi exit ${ERROR} system-validators-1.4.0/hack/verify-shellcheck.sh000077500000000000000000000027011401116447000220600ustar00rootroot00000000000000#!/usr/bin/env bash # Copyright 2019 The Kubernetes Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # set -o errexit set -o nounset set -o pipefail # shellcheck source=/dev/null source "$(dirname "$0")/utils.sh" ROOT_PATH=$(get_root_path) # create a temporary directory TMP_DIR=$(mktemp -d) # cleanup on exit cleanup() { echo "Cleaning up..." rm -rf "${TMP_DIR}" } trap cleanup EXIT # install shellcheck (Linux-x64 only!) cd "${TMP_DIR}" || exit VERSION="shellcheck-v0.6.0" DOWNLOAD_FILE="${VERSION}.linux.x86_64.tar.xz" wget https://storage.googleapis.com/shellcheck/"${DOWNLOAD_FILE}" tar xf "${DOWNLOAD_FILE}" cd "${VERSION}" || exit echo "Running shellcheck..." cd "${ROOT_PATH}" || exit OUT="${TMP_DIR}/out.log" FILES=$(git ls-files | grep -v "vendor\/" | grep ".sh") while read -r file; do "${TMP_DIR}/${VERSION}/shellcheck" "$file" >> "${OUT}" 2>&1 done <<< "$FILES" if [[ -s "${OUT}" ]]; then echo "Found errors:" cat "${OUT}" exit 1 fi system-validators-1.4.0/hack/verify-spelling.sh000077500000000000000000000031511401116447000215700ustar00rootroot00000000000000#!/usr/bin/env bash # Copyright 2019 The Kubernetes Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set -o errexit set -o nounset set -o pipefail # shellcheck source=/dev/null source "$(dirname "$0")/utils.sh" # cd to the root path cd_root_path # create a temporary directory TMP_DIR=$(mktemp -d) # cleanup exitHandler() ( echo "Cleaning up..." rm -rf "${TMP_DIR}" ) trap exitHandler EXIT # pull misspell export GO111MODULE=on URL="https://github.com/client9/misspell" echo "Cloning ${URL} in ${TMP_DIR}..." git clone --quiet --depth=1 "${URL}" "${TMP_DIR}" pushd "${TMP_DIR}" > /dev/null go mod init misspell popd > /dev/null # build misspell BIN_PATH="${TMP_DIR}/cmd/misspell" pushd "${BIN_PATH}" > /dev/null echo "Building misspell..." go build > /dev/null popd > /dev/null # check spelling RES=0 ERROR_LOG="${TMP_DIR}/errors.log" echo "Checking spelling..." git ls-files | grep -v -e vendor | xargs "${BIN_PATH}/misspell" > "${ERROR_LOG}" if [[ -s "${ERROR_LOG}" ]]; then sed 's/^/error: /' "${ERROR_LOG}" # add 'error' to each line to highlight in e2e status echo "Found spelling errors!" RES=1 fi exit "${RES}" system-validators-1.4.0/hack/verify-staticcheck.sh000077500000000000000000000024631401116447000222450ustar00rootroot00000000000000#!/usr/bin/env bash # Copyright 2019 The Kubernetes Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # CI script to run staticcheck set -o errexit set -o nounset set -o pipefail # shellcheck source=/dev/null source "$(dirname "$0")/utils.sh" # cd to the root path REPO_PATH=$(get_root_path) # create a temporary directory TMP_DIR=$(mktemp -d) # cleanup exitHandler() ( echo "Cleaning up..." rm -rf "${TMP_DIR}" ) trap exitHandler EXIT # pull the source code and build the binary cd "${TMP_DIR}" URL="http://github.com/dominikh/go-tools" echo "Cloning ${URL} in ${TMP_DIR}..." git clone --quiet --depth=1 "${URL}" . echo "Building staticcheck..." export GO111MODULE=on go build -o ./bin/staticheck ./cmd/staticcheck # run the binary cd "${REPO_PATH}" echo "Running staticcheck..." "${TMP_DIR}/bin/staticheck" ./... system-validators-1.4.0/hack/verify-whitespace.sh000077500000000000000000000026411401116447000221120ustar00rootroot00000000000000#!/usr/bin/env bash # Copyright 2019 The Kubernetes Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set -o nounset set -o pipefail # shellcheck source=/dev/null source "$(dirname "$0")/utils.sh" # cd to the root path cd_root_path echo "Verifying trailing whitespace..." TRAILING="$(grep -rnI '[[:blank:]]$' . | grep -v -e "\.git" -v -e "vendor\/" -v -e "\.svg")" ERR="0" if [[ ! -z "$TRAILING" ]]; then echo "Found trailing whitespace in the follow files:" echo "${TRAILING}" ERR="1" fi echo -e "Verifying new lines at end of files..." FILES="$(git ls-files | grep -I -v -e "vendor\/" -v -e "\.svg")" while read -r LINE; do grep -qI . "${LINE}" || continue # skip binary files c="$(tail -c 1 "${LINE}")" if [[ "$c" != "" ]]; then echo "${LINE}: no newline at the end of file" ERR=1 fi done <<< "${FILES}" if [[ "$ERR" == "1" ]]; then echo "Found whitespace errors!" exit 1 fi system-validators-1.4.0/validators/000077500000000000000000000000001401116447000173545ustar00rootroot00000000000000system-validators-1.4.0/validators/cgroup_validator_linux.go000066400000000000000000000110731401116447000244700ustar00rootroot00000000000000// +build linux /* Copyright 2016 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package system import ( "bufio" "io/ioutil" "os" "path/filepath" "strings" "github.com/pkg/errors" "golang.org/x/sys/unix" ) var _ Validator = &CgroupsValidator{} // CgroupsValidator validates cgroup configuration. type CgroupsValidator struct { Reporter Reporter } // Name is part of the system.Validator interface. func (c *CgroupsValidator) Name() string { return "cgroups" } const ( cgroupsConfigPrefix = "CGROUPS_" unifiedMountpoint = "/sys/fs/cgroup" ) // Validate is part of the system.Validator interface. func (c *CgroupsValidator) Validate(spec SysSpec) (warns, errs []error) { // Get the subsystems from /sys/fs/cgroup/cgroup.controllers when cgroup v2 is used. // /proc/cgroups is meaningless for v2 // https://github.com/torvalds/linux/blob/v5.3/Documentation/admin-guide/cgroup-v2.rst#deprecated-v1-core-features var st unix.Statfs_t var err error if err := unix.Statfs(unifiedMountpoint, &st); err != nil { return nil, []error{errors.Wrap(err, "cannot statfs the cgroupv2 root")} } var requiredCgroupSpec []string var optionalCgroupSpec []string var subsystems []string if st.Type == unix.CGROUP2_SUPER_MAGIC { subsystems, err = c.getCgroupV2Subsystems() if err != nil { return nil, []error{errors.Wrap(err, "failed to get cgroup v2 subsystems")} } requiredCgroupSpec = spec.CgroupsV2 optionalCgroupSpec = spec.CgroupsV2Optional } else { subsystems, err = c.getCgroupV1Subsystems() if err != nil { return nil, []error{errors.Wrap(err, "failed to get cgroup v1 subsystems")} } requiredCgroupSpec = spec.Cgroups optionalCgroupSpec = spec.CgroupsOptional } if missingRequired := c.validateCgroupSubsystems(requiredCgroupSpec, subsystems, true); len(missingRequired) != 0 { errs = []error{errors.Errorf("missing required cgroups: %s", strings.Join(missingRequired, " "))} } if missingOptional := c.validateCgroupSubsystems(optionalCgroupSpec, subsystems, false); len(missingOptional) != 0 { warns = []error{errors.Errorf("missing optional cgroups: %s", strings.Join(missingOptional, " "))} } return } // validateCgroupSubsystems returns a list with the missing cgroups in the cgroup func (c *CgroupsValidator) validateCgroupSubsystems(cgroups, subsystems []string, required bool) []string { var missing []string for _, cgroup := range cgroups { found := false for _, subsystem := range subsystems { if cgroup == subsystem { found = true break } } item := cgroupsConfigPrefix + strings.ToUpper(cgroup) if found { c.Reporter.Report(item, "enabled", good) continue } else if required { c.Reporter.Report(item, "missing", bad) } else { c.Reporter.Report(item, "missing", warn) } missing = append(missing, cgroup) } return missing } func (c *CgroupsValidator) getCgroupV1Subsystems() ([]string, error) { // Get the subsystems from /proc/cgroups when cgroup v1 is used. f, err := os.Open("/proc/cgroups") if err != nil { return nil, err } defer f.Close() subsystems := []string{} s := bufio.NewScanner(f) for s.Scan() { if err := s.Err(); err != nil { return nil, err } text := s.Text() if text[0] != '#' { parts := strings.Fields(text) if len(parts) >= 4 && parts[3] != "0" { subsystems = append(subsystems, parts[0]) } } } return subsystems, nil } func (c *CgroupsValidator) getCgroupV2Subsystems() ([]string, error) { // Some controllers are implicitly enabled by the kernel. // Those controllers do not appear in /sys/fs/cgroup/cgroup.controllers. // https://github.com/torvalds/linux/blob/v5.3/kernel/cgroup/cgroup.c#L433-L434 // We assume these are always available, as it is hard to detect availability. // So, we hardcode the following as "pseudo" controllers. // - devices: implemented in kernel 4.15 // - freezer: implemented in kernel 5.2 pseudo := []string{"devices", "freezer"} data, err := ioutil.ReadFile(filepath.Join(unifiedMountpoint, "cgroup.controllers")) if err != nil { return nil, err } subsystems := append(pseudo, strings.Fields(string(data))...) return subsystems, nil } system-validators-1.4.0/validators/cgroup_validator_other.go000066400000000000000000000020101401116447000244410ustar00rootroot00000000000000// +build !linux /* Copyright 2020 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package system // NOOP for non-Linux OSes. var _ Validator = &CgroupsValidator{} // CgroupsValidator validates cgroup configuration. type CgroupsValidator struct { Reporter Reporter } // Validate is part of the system.Validator interface. func (c *CgroupsValidator) Validate(spec SysSpec) (warns, errs []error) { return } // Name is part of the system.Validator interface. func (c *CgroupsValidator) Name() string { return "cgroups" } system-validators-1.4.0/validators/cgroup_validator_test.go000066400000000000000000000056771401116447000243250ustar00rootroot00000000000000// +build linux /* Copyright 2016 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package system import ( "testing" "github.com/stretchr/testify/assert" ) func TestValidateCgroupSubsystem(t *testing.T) { // hardcoded cgroup v2 subsystems pseudoSubsystems := []string{"devices", "freezer"} v := &CgroupsValidator{ Reporter: DefaultReporter, } for desc, test := range map[string]struct { subsystems []string cgroupSpec []string required bool missing []string }{ "missing required cgroup subsystem should report missing": { cgroupSpec: []string{"system1", "system2"}, subsystems: []string{"system1"}, required: true, missing: []string{"system2"}, }, "missing optional cgroup subsystem should report missing": { cgroupSpec: []string{"system1", "system2"}, subsystems: []string{"system1"}, required: false, missing: []string{"system2"}, }, "extra cgroup subsystems should not report missing": { cgroupSpec: []string{"system1", "system2"}, subsystems: []string{"system1", "system2", "system3"}, required: true, missing: nil, }, "subsystems the same with spec should not report missing": { cgroupSpec: []string{"system1"}, subsystems: []string{"system1", "system2"}, required: false, missing: nil, }, "missing required cgroup subsystem when pseudo hardcoded subsystems are set": { cgroupSpec: []string{"system1", "devices", "freezer"}, subsystems: append(pseudoSubsystems), required: true, missing: []string{"system1"}, }, "missing optional cgroup subsystem when pseudo hardcoded subsystems are set": { cgroupSpec: []string{"system1", "devices", "freezer"}, subsystems: append(pseudoSubsystems), required: false, missing: []string{"system1"}, }, "extra cgroup subsystems when pseudo hardcoded subsystems are set": { cgroupSpec: []string{"system1", "devices", "freezer"}, subsystems: append(pseudoSubsystems, "system1", "system2"), required: true, missing: nil, }, "matching list of cgroup subsystems including pseudo hardcoded subsystems": { cgroupSpec: []string{"system1", "devices", "freezer"}, subsystems: append(pseudoSubsystems, "system1"), required: false, missing: nil, }, } { t.Run(desc, func(t *testing.T) { missing := v.validateCgroupSubsystems(test.cgroupSpec, test.subsystems, test.required) assert.Equal(t, test.missing, missing, "%q: Expect error not to occur with cgroup", desc) }) } } system-validators-1.4.0/validators/docker_validator.go000066400000000000000000000101211401116447000232120ustar00rootroot00000000000000/* Copyright 2016 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package system import ( "encoding/json" "os/exec" "regexp" "strings" "github.com/pkg/errors" ) var _ Validator = &DockerValidator{} // DockerValidator validates docker configuration. type DockerValidator struct { Reporter Reporter } // dockerInfo holds a local subset of the Info struct from // https://github.com/docker/cli/blob/master/cli/command/system/info.go // and https://github.com/moby/moby/blob/master/api/types/types.go // The JSON output from 'docker info' should map to this struct. type dockerInfo struct { Driver string `json:"Driver"` ServerVersion string `json:"ServerVersion"` ServerErrors []string `json:",omitempty"` } // Name is part of the system.Validator interface. func (d *DockerValidator) Name() string { return "docker" } const ( dockerConfigPrefix = "DOCKER_" latestValidatedDockerVersion = "20.10" ) // Validate is part of the system.Validator interface. // TODO(random-liu): Add more validating items. func (d *DockerValidator) Validate(spec SysSpec) ([]error, []error) { if spec.RuntimeSpec.DockerSpec == nil { // If DockerSpec is not specified, assume current runtime is not // docker, skip the docker configuration validation. return nil, nil } // Run 'docker info' with a JSON output and unmarshal it into a dockerInfo object info := dockerInfo{} out, err := exec.Command("docker", "info", "--format", "{{json .}}").CombinedOutput() if err != nil { return nil, []error{errors.Errorf(`failed executing "docker info --format '{{json .}}'"\noutput: %s\nerror: %v`, string(out), err)} } if err := d.unmarshalDockerInfo(out, &info); err != nil { return nil, []error{err} } // validate the resulted docker info object against the spec return d.validateDockerInfo(spec.RuntimeSpec.DockerSpec, info) } func (d *DockerValidator) unmarshalDockerInfo(b []byte, info *dockerInfo) error { if err := json.Unmarshal(b, &info); err != nil { return errors.Wrapf(err, "could not unmarshal the JSON output of 'docker info':\n%s\n", b) } return nil } func (d *DockerValidator) validateDockerInfo(spec *DockerSpec, info dockerInfo) ([]error, []error) { // Validate docker version. if info.ServerErrors != nil { return nil, []error{errors.Errorf("error verifying Docker info: %q", strings.Join(info.ServerErrors, `", "`))} } matched := false for _, v := range spec.Version { r := regexp.MustCompile(v) if r.MatchString(info.ServerVersion) { d.Reporter.Report(dockerConfigPrefix+"VERSION", info.ServerVersion, good) matched = true } } if !matched { // If it's of the new Docker version scheme but didn't match above, it // must be a newer version than the most recently validated one. ver := `\d{2}\.\d+\.\d+(?:-[a-z]{2})?` r := regexp.MustCompile(ver) if r.MatchString(info.ServerVersion) { d.Reporter.Report(dockerConfigPrefix+"VERSION", info.ServerVersion, good) w := errors.Errorf( "this Docker version is not on the list of validated versions: %s. Latest validated version: %s", info.ServerVersion, latestValidatedDockerVersion, ) return []error{w}, nil } d.Reporter.Report(dockerConfigPrefix+"VERSION", info.ServerVersion, bad) return nil, []error{errors.Errorf("unsupported docker version: %s", info.ServerVersion)} } // Validate graph driver. item := dockerConfigPrefix + "GRAPH_DRIVER" for _, gd := range spec.GraphDriver { if info.Driver == gd { d.Reporter.Report(item, info.Driver, good) return nil, nil } } d.Reporter.Report(item, info.Driver, bad) return nil, []error{errors.Errorf("unsupported graph driver: %s", info.Driver)} } system-validators-1.4.0/validators/docker_validator_test.go000066400000000000000000000114371401116447000242640ustar00rootroot00000000000000/* Copyright 2016 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package system import ( "reflect" "testing" "github.com/stretchr/testify/assert" ) func TestValidateDockerInfo(t *testing.T) { v := &DockerValidator{ Reporter: DefaultReporter, } spec := &DockerSpec{ Version: []string{`1\.13\..*`, `17\.0[3,6,9]\..*`, `18\.0[6,9]\..*`, `19\.03\..*`, `20\.10\..*`}, GraphDriver: []string{"driver_1", "driver_2"}, } for _, test := range []struct { name string info dockerInfo err bool warn bool }{ { name: "unsupported Docker version 1.12.1", info: dockerInfo{Driver: "driver_2", ServerVersion: "1.12.1"}, err: true, warn: false, }, { name: "unsupported driver", info: dockerInfo{Driver: "bad_driver", ServerVersion: "1.13.1"}, err: true, warn: false, }, { name: "valid Docker version 1.13.1", info: dockerInfo{Driver: "driver_1", ServerVersion: "1.13.1"}, err: false, warn: false, }, { name: "valid Docker version 17.03.0-ce", info: dockerInfo{Driver: "driver_2", ServerVersion: "17.03.0-ce"}, err: false, warn: false, }, { name: "valid Docker version 17.06.0-ce", info: dockerInfo{Driver: "driver_2", ServerVersion: "17.06.0-ce"}, err: false, warn: false, }, { name: "valid Docker version 17.09.0-ce", info: dockerInfo{Driver: "driver_2", ServerVersion: "17.09.0-ce"}, err: false, warn: false, }, { name: "valid Docker version 18.06.0-ce", info: dockerInfo{Driver: "driver_2", ServerVersion: "18.06.0-ce"}, err: false, warn: false, }, { name: "valid Docker version 18.09.1-ce", info: dockerInfo{Driver: "driver_2", ServerVersion: "18.09.1-ce"}, err: false, warn: false, }, { name: "valid Docker version 19.03.1-ce", info: dockerInfo{Driver: "driver_2", ServerVersion: "19.03.1-ce"}, err: false, warn: false, }, { name: "Docker version 19.06.0 is not in the list of validated versions", info: dockerInfo{Driver: "driver_2", ServerVersion: "19.06.0"}, err: false, warn: true, }, { name: "valid Docker version 20.10.3", info: dockerInfo{Driver: "driver_2", ServerVersion: "20.10.3"}, err: false, warn: false, }, { name: "Docker daemon not available", info: dockerInfo{ServerErrors: []string{"Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running"}}, err: true, warn: false, }, } { t.Run(test.name, func(t *testing.T) { warn, err := v.validateDockerInfo(spec, test.info) if !test.err { assert.Nil(t, err, "Expect error not to occur with docker info %+v", test.info) } else { assert.NotNil(t, err, "Expect error to occur with docker info %+v", test.info) } if !test.warn { assert.Nil(t, warn, "Expect error not to occur with docker info %+v", test.info) } else { assert.NotNil(t, warn, "Expect error to occur with docker info %+v", test.info) } }) } } func TestUnmarshalDockerInfo(t *testing.T) { v := &DockerValidator{} testCases := []struct { name string input string expectedInfo dockerInfo expectedError bool }{ { name: "valid: expected dockerInfo is valid", input: `{ "Driver":"foo", "ServerVersion":"bar" }`, expectedInfo: dockerInfo{Driver: "foo", ServerVersion: "bar"}, }, { name: "valid: expected dockerInfo is valid although daemon is not running", input: `{ "ServerErrors":["Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?"]}`, expectedInfo: dockerInfo{ServerErrors: []string{"Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?"}}, }, { name: "invalid: the JSON input is not valid", input: `{ "Driver":"foo"`, expectedError: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { var err error info := dockerInfo{} if err = v.unmarshalDockerInfo([]byte(tc.input), &info); (err != nil) != tc.expectedError { t.Fatalf("failed unmarshaling; expected error: %v, got: %v, error: %v", tc.expectedError, (err != nil), err) } if err != nil { return } if !reflect.DeepEqual(tc.expectedInfo, info) { t.Fatalf("dockerInfo do not match, expected: %#v, got: %#v", tc.expectedInfo, info) } }) } } system-validators-1.4.0/validators/kernel_validator.go000066400000000000000000000175521401116447000232420ustar00rootroot00000000000000/* Copyright 2016 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package system import ( "bufio" "bytes" "compress/gzip" "fmt" "io" "io/ioutil" "os" "os/exec" "path/filepath" "regexp" "strings" "github.com/pkg/errors" ) var _ Validator = &KernelValidator{} // KernelValidator validates kernel. Currently only validate kernel version // and kernel configuration. type KernelValidator struct { kernelRelease string Reporter Reporter } // Name is part of the system.Validator interface. func (k *KernelValidator) Name() string { return "kernel" } // kConfigOption is the possible kernel config option. type kConfigOption string const ( builtIn kConfigOption = "y" asModule kConfigOption = "m" leftOut kConfigOption = "n" // validKConfigRegex is the regex matching kernel configuration line. validKConfigRegex = "^CONFIG_[A-Z0-9_]+=[myn]" // kernelConfigPrefix is the prefix of kernel configuration. kernelConfigPrefix = "CONFIG_" ) // Validate is part of the system.Validator interface. func (k *KernelValidator) Validate(spec SysSpec) ([]error, []error) { helper := KernelValidatorHelperImpl{} release, err := helper.GetKernelReleaseVersion() if err != nil { return nil, []error{errors.Wrap(err, "failed to get kernel release")} } k.kernelRelease = release var errs []error if err = k.validateKernelVersion(spec.KernelSpec); err != nil { errs = append(errs, err) } // only validate kernel config when necessary (currently no kernel config for windows) if len(spec.KernelSpec.Required) > 0 || len(spec.KernelSpec.Forbidden) > 0 || len(spec.KernelSpec.Optional) > 0 { if err = k.validateKernelConfig(spec.KernelSpec); err != nil { errs = append(errs, err) } } return nil, errs } // validateKernelVersion validates the kernel version. func (k *KernelValidator) validateKernelVersion(kSpec KernelSpec) error { versionRegexps := kSpec.Versions for _, versionRegexp := range versionRegexps { r := regexp.MustCompile(versionRegexp) if r.MatchString(k.kernelRelease) { k.Reporter.Report("KERNEL_VERSION", k.kernelRelease, good) return nil } } k.Reporter.Report("KERNEL_VERSION", k.kernelRelease, bad) return errors.Errorf("unsupported kernel release: %s", k.kernelRelease) } // validateKernelConfig validates the kernel configurations. func (k *KernelValidator) validateKernelConfig(kSpec KernelSpec) error { allConfig, err := k.getKernelConfig() if err != nil { return errors.Wrap(err, "failed to parse kernel config") } return k.validateCachedKernelConfig(allConfig, kSpec) } // validateCachedKernelConfig validates the kernel confgiurations cached in internal data type. func (k *KernelValidator) validateCachedKernelConfig(allConfig map[string]kConfigOption, kSpec KernelSpec) error { badConfigs := []string{} // reportAndRecord is a helper function to record bad config when // report. reportAndRecord := func(name, msg, desc string, result ValidationResultType) { if result == bad { badConfigs = append(badConfigs, name) } // report description when the config is bad or warn. if result != good && desc != "" { msg = msg + " - " + desc } k.Reporter.Report(name, msg, result) } const ( required = iota optional forbidden ) validateOpt := func(config KernelConfig, expect int) { var found, missing ValidationResultType switch expect { case required: found, missing = good, bad case optional: found, missing = good, warn case forbidden: found, missing = bad, good } var name string var opt kConfigOption var ok bool for _, name = range append([]string{config.Name}, config.Aliases...) { name = kernelConfigPrefix + name if opt, ok = allConfig[name]; ok { break } } if !ok { reportAndRecord(name, "not set", config.Description, missing) return } switch opt { case builtIn: reportAndRecord(name, "enabled", config.Description, found) case asModule: reportAndRecord(name, "enabled (as module)", config.Description, found) case leftOut: reportAndRecord(name, "disabled", config.Description, missing) default: reportAndRecord(name, fmt.Sprintf("unknown option: %s", opt), config.Description, missing) } } for _, config := range kSpec.Required { validateOpt(config, required) } for _, config := range kSpec.Optional { validateOpt(config, optional) } for _, config := range kSpec.Forbidden { validateOpt(config, forbidden) } if len(badConfigs) > 0 { return errors.Errorf("unexpected kernel config: %s", strings.Join(badConfigs, " ")) } return nil } // getKernelConfigReader search kernel config file in a predefined list. Once the kernel config // file is found it will read the configurations into a byte buffer and return. If the kernel // config file is not found, it will try to load kernel config module and retry again. func (k *KernelValidator) getKernelConfigReader() (io.Reader, error) { possibePaths := []string{ "/proc/config.gz", "/boot/config-" + k.kernelRelease, "/usr/src/linux-" + k.kernelRelease + "/.config", "/usr/src/linux/.config", "/usr/lib/modules/" + k.kernelRelease + "/config", "/usr/lib/ostree-boot/config-" + k.kernelRelease, "/usr/lib/kernel/config-" + k.kernelRelease, "/usr/src/linux-headers-" + k.kernelRelease + "/.config", "/lib/modules/" + k.kernelRelease + "/build/.config", } configsModule := "configs" modprobeCmd := "modprobe" // loadModule indicates whether we've tried to load kernel config module ourselves. loadModule := false for { for _, path := range possibePaths { _, err := os.Stat(path) if err != nil { continue } // Buffer the whole file, so that we can close the file and unload // kernel config module in this function. b, err := ioutil.ReadFile(path) if err != nil { return nil, err } var r io.Reader r = bytes.NewReader(b) // This is a gzip file (config.gz), unzip it. if filepath.Ext(path) == ".gz" { r, err = gzip.NewReader(r) if err != nil { return nil, err } } return r, nil } // If we've tried to load kernel config module, break and return error. if loadModule { break } // If the kernel config file is not found, try to load the kernel // config module and check again. output, err := exec.Command(modprobeCmd, configsModule).CombinedOutput() if err != nil { return nil, errors.Wrapf(err, "unable to load kernel module: %q, output: %q, err", configsModule, output) } // Unload the kernel config module to make sure the validation have no side effect. defer exec.Command(modprobeCmd, "-r", configsModule).Run() loadModule = true } return nil, errors.Errorf("no config path in %v is available", possibePaths) } // getKernelConfig gets kernel config from kernel config file and convert kernel config to internal type. func (k *KernelValidator) getKernelConfig() (map[string]kConfigOption, error) { r, err := k.getKernelConfigReader() if err != nil { return nil, err } return k.parseKernelConfig(r) } // parseKernelConfig converts kernel config to internal type. func (k *KernelValidator) parseKernelConfig(r io.Reader) (map[string]kConfigOption, error) { config := map[string]kConfigOption{} regex := regexp.MustCompile(validKConfigRegex) s := bufio.NewScanner(r) for s.Scan() { if err := s.Err(); err != nil { return nil, err } line := strings.TrimSpace(s.Text()) if !regex.MatchString(line) { continue } fields := strings.Split(line, "=") config[fields[0]] = kConfigOption(fields[1][0]) } return config, nil } system-validators-1.4.0/validators/kernel_validator_helper.go000066400000000000000000000015161401116447000245720ustar00rootroot00000000000000/* Copyright 2017 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package system // KernelValidatorHelper is an interface intended to help with os specific kernel validation type KernelValidatorHelper interface { // GetKernelReleaseVersion gets the current kernel release version of the system GetKernelReleaseVersion() (string, error) } system-validators-1.4.0/validators/kernel_validator_test.go000066400000000000000000000130631401116447000242720ustar00rootroot00000000000000/* Copyright 2016 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package system import ( "bytes" "testing" "github.com/stretchr/testify/assert" ) func TestValidateKernelVersion(t *testing.T) { v := &KernelValidator{ Reporter: DefaultReporter, } // Currently, testRegex is align with DefaultSysSpec.KernelVersion, but in the future // they may be different. // This is fine, because the test mainly tests the kernel version validation logic, // not the DefaultSysSpec. The DefaultSysSpec should be tested with node e2e. testRegex := []string{`^3\.[1-9][0-9].*$`, `^([4-9]|[1-9][0-9]+)\.([0-9]+)\.([0-9]+).*$`} for _, test := range []struct { name string version string err bool }{ { name: "3.19.9-99-test first version regex matches", version: "3.19.9-99-test", err: false, }, { name: "4.4.14+ one of version regexes matches", version: "4.4.14+", err: false, }, { name: "2.0.0 no version regex matches", version: "2.0.0", err: true, }, { name: "5.0.0 one of version regexes matches", version: "5.0.0", err: false, }, { name: "10.21.1 one of version regexes matches", version: "10.21.1", err: false, }, { name: "99.12.12 one of version regexes matches", version: "99.12.12", err: false, }, { name: "3.9.0 no version regex matches", version: "3.9.0", err: true, }, } { t.Run(test.name, func(t *testing.T) { v.kernelRelease = test.version err := v.validateKernelVersion(KernelSpec{Versions: testRegex}) if !test.err { assert.Nil(t, err, "Expect error not to occur with kernel version %q", test.version) } else { assert.NotNil(t, err, "Expect error to occur with kenrel version %q", test.version) } }) } } func TestValidateCachedKernelConfig(t *testing.T) { v := &KernelValidator{ Reporter: DefaultReporter, } testKernelSpec := KernelSpec{ Required: []KernelConfig{{Name: "REQUIRED_1"}, {Name: "REQUIRED_2", Aliases: []string{"ALIASE_REQUIRED_2"}}}, Optional: []KernelConfig{{Name: "OPTIONAL_1"}, {Name: "OPTIONAL_2"}}, Forbidden: []KernelConfig{ {Name: "FORBIDDEN_1", Description: "TEST FORBIDDEN"}, {Name: "FORBIDDEN_2", Aliases: []string{"ALIASE_FORBIDDEN_2"}}, }, } for _, test := range []struct { desc string config map[string]kConfigOption err bool }{ { desc: "meet all required configurations should not report error", config: map[string]kConfigOption{ "REQUIRED_1": builtIn, "REQUIRED_2": asModule, }, err: false, }, { desc: "one required configuration disabled should report error", config: map[string]kConfigOption{ "REQUIRED_1": leftOut, "REQUIRED_2": builtIn, }, err: true, }, { desc: "one required configuration missing should report error", config: map[string]kConfigOption{ "REQUIRED_1": builtIn, }, err: true, }, { desc: "alias of required configuration should not report error", config: map[string]kConfigOption{ "REQUIRED_1": builtIn, "ALIASE_REQUIRED_2": asModule, }, err: false, }, { desc: "optional configuration set or not should not report error", config: map[string]kConfigOption{ "REQUIRED_1": builtIn, "REQUIRED_2": asModule, "OPTIONAL_1": builtIn, }, err: false, }, { desc: "forbidden configuration disabled should not report error", config: map[string]kConfigOption{ "REQUIRED_1": builtIn, "REQUIRED_2": asModule, "FORBIDDEN_1": leftOut, }, err: false, }, { desc: "forbidden configuration built-in should report error", config: map[string]kConfigOption{ "REQUIRED_1": builtIn, "REQUIRED_2": asModule, "FORBIDDEN_1": builtIn, }, err: true, }, { desc: "forbidden configuration built as module should report error", config: map[string]kConfigOption{ "REQUIRED_1": builtIn, "REQUIRED_2": asModule, "FORBIDDEN_1": asModule, }, err: true, }, { desc: "alias of forbidden configuration should report error", config: map[string]kConfigOption{ "REQUIRED_1": builtIn, "REQUIRED_2": asModule, "ALIASE_FORBIDDEN_2": asModule, }, err: true, }, } { t.Run(test.desc, func(t *testing.T) { // Add kernel config prefix. for k, v := range test.config { delete(test.config, k) test.config[kernelConfigPrefix+k] = v } err := v.validateCachedKernelConfig(test.config, testKernelSpec) if !test.err { assert.Nil(t, err, "Expect error not to occur with kernel config %q", test.config) } else { assert.NotNil(t, err, "Expect error to occur with kenrel config %q", test.config) } }) } } func TestValidateParseKernelConfig(t *testing.T) { config := `CONFIG_1=y CONFIG_2=m CONFIG_3=n` expected := map[string]kConfigOption{ "CONFIG_1": builtIn, "CONFIG_2": asModule, "CONFIG_3": leftOut, } v := &KernelValidator{ Reporter: DefaultReporter, } got, err := v.parseKernelConfig(bytes.NewReader([]byte(config))) assert.Nil(t, err, "Expect error not to occur when parse kernel configuration %q", config) assert.Equal(t, expected, got) } system-validators-1.4.0/validators/os_validator.go000066400000000000000000000027041401116447000223740ustar00rootroot00000000000000/* Copyright 2016 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package system import ( "os/exec" "strings" "github.com/pkg/errors" ) var _ Validator = &OSValidator{} // OSValidator validates OS. type OSValidator struct { Reporter Reporter } // Name is part of the system.Validator interface. func (o *OSValidator) Name() string { return "os" } // Validate is part of the system.Validator interface. func (o *OSValidator) Validate(spec SysSpec) ([]error, []error) { os, err := exec.Command("uname").CombinedOutput() if err != nil { return nil, []error{errors.Wrap(err, "failed to get os name")} } if err = o.validateOS(strings.TrimSpace(string(os)), spec.OS); err != nil { return nil, []error{err} } return nil, nil } func (o *OSValidator) validateOS(os, specOS string) error { if os != specOS { o.Reporter.Report("OS", os, bad) return errors.Errorf("unsupported operating system: %s", os) } o.Reporter.Report("OS", os, good) return nil } system-validators-1.4.0/validators/os_validator_test.go000066400000000000000000000022741401116447000234350ustar00rootroot00000000000000/* Copyright 2016 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package system import ( "testing" "github.com/stretchr/testify/assert" ) func TestValidateOS(t *testing.T) { v := &OSValidator{ Reporter: DefaultReporter, } specOS := "Linux" for _, test := range []struct { os string err bool }{ { os: "Linux", err: false, }, { os: "Windows", err: true, }, { os: "Darwin", err: true, }, } { t.Run(test.os, func(t *testing.T) { err := v.validateOS(test.os, specOS) if !test.err { assert.Nil(t, err, "Expect error not to occur with os %q", test.os) } else { assert.NotNil(t, err, "Expect error to occur with os %q", test.os) } }) } } system-validators-1.4.0/validators/package_validator_linux.go000066400000000000000000000232511401116447000245650ustar00rootroot00000000000000// +build linux /* Copyright 2017 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package system import ( "fmt" "io/ioutil" "os/exec" "strings" "github.com/blang/semver" "github.com/pkg/errors" ) // semVerDotsCount is the number of dots in a valid semantic version. const semVerDotsCount int = 2 // packageManager is an interface that abstracts the basic operations of a // package manager. type packageManager interface { // getPackageVersion returns the version of the package given the // packageName, or an error if no such package exists. getPackageVersion(packageName string) (string, error) } // newPackageManager returns the package manager on the running machine, and an // error if no package managers is available. func newPackageManager() (packageManager, error) { if m, ok := newDPKG(); ok { return m, nil } return nil, errors.New("failed to find package manager") } // dpkg implements packageManager. It uses "dpkg-query" to retrieve package // information. type dpkg struct{} // newDPKG returns a Debian package manager. It returns (nil, false) if no such // package manager exists on the running machine. func newDPKG() (packageManager, bool) { _, err := exec.LookPath("dpkg-query") if err != nil { return nil, false } return dpkg{}, true } // getPackageVersion returns the upstream package version for the package given // the packageName, and an error if no such package exists. func (dpkg) getPackageVersion(packageName string) (string, error) { output, err := exec.Command("dpkg-query", "--show", "--showformat='${Version}'", packageName).Output() if err != nil { return "", errors.Wrap(err, "dpkg-query failed") } version := extractUpstreamVersion(string(output)) if version == "" { return "", errors.New("no version information") } return version, nil } // packageValidator implements the Validator interface. It validates packages // and their versions. type packageValidator struct { reporter Reporter kernelRelease string osDistro string } // Name returns the name of the package validator. func (validator *packageValidator) Name() string { return "package" } // Validate checks packages and their versions against the spec using the // package manager on the running machine, and returns an error on any // package/version mismatch. func (validator *packageValidator) Validate(spec SysSpec) ([]error, []error) { if len(spec.PackageSpecs) == 0 { return nil, nil } var err error if validator.kernelRelease, err = getKernelRelease(); err != nil { return nil, []error{err} } if validator.osDistro, err = getOSDistro(); err != nil { return nil, []error{err} } manager, err := newPackageManager() if err != nil { return nil, []error{err} } specs := applyPackageSpecOverride(spec.PackageSpecs, spec.PackageSpecOverrides, validator.osDistro) return validator.validate(specs, manager) } // Validate checks packages and their versions against the packageSpecs using // the packageManager, and returns an error on any package/version mismatch. func (validator *packageValidator) validate(packageSpecs []PackageSpec, manager packageManager) ([]error, []error) { var errs []error for _, spec := range packageSpecs { // Substitute variables in package name. packageName := resolvePackageName(spec.Name, validator.kernelRelease) nameWithVerRange := fmt.Sprintf("%s (%s)", packageName, spec.VersionRange) // Get the version of the package on the running machine. version, err := manager.getPackageVersion(packageName) if err != nil { errs = append(errs, err) validator.reporter.Report(nameWithVerRange, "not installed", bad) continue } // Version requirement will not be enforced if version range is // not specified in the spec. if spec.VersionRange == "" { validator.reporter.Report(packageName, version, good) continue } // Convert both the version range in the spec and the version returned // from package manager to semantic version format, and then check if // the version is in the range. sv, err := semver.Make(toSemVer(version)) if err != nil { errs = append(errs, err) validator.reporter.Report(nameWithVerRange, "internal error", bad) continue } versionRange := semver.MustParseRange(toSemVerRange(spec.VersionRange)) if versionRange(sv) { validator.reporter.Report(nameWithVerRange, version, good) } else { errs = append(errs, errors.Errorf("package \"%s %s\" does not meet the spec \"%s (%s)\"", packageName, sv, packageName, spec.VersionRange)) validator.reporter.Report(nameWithVerRange, version, bad) } } return nil, errs } // getKernelRelease returns the kernel release of the local machine. func getKernelRelease() (string, error) { output, err := exec.Command("uname", "-r").Output() if err != nil { return "", errors.Wrap(err, "failed to get kernel release") } return strings.TrimSpace(string(output)), nil } // getOSDistro returns the OS distro of the local machine. func getOSDistro() (string, error) { f := "/etc/lsb-release" b, err := ioutil.ReadFile(f) if err != nil { return "", errors.Wrapf(err, "failed to read %q", f) } content := string(b) switch { case strings.Contains(content, "Ubuntu"): return "ubuntu", nil case strings.Contains(content, "Chrome OS"): return "cos", nil case strings.Contains(content, "CoreOS"): return "coreos", nil default: return "", errors.Errorf("failed to get OS distro: %s", content) } } // resolvePackageName substitutes the variables in the packageName with the // local information. // E.g., "linux-headers-${KERNEL_RELEASE}" -> "linux-headers-4.4.0-75-generic". func resolvePackageName(packageName string, kernelRelease string) string { packageName = strings.Replace(packageName, "${KERNEL_RELEASE}", kernelRelease, -1) return packageName } // applyPackageSpecOverride applies the package spec overrides for the given // osDistro to the packageSpecs and returns the applied result. func applyPackageSpecOverride(packageSpecs []PackageSpec, overrides []PackageSpecOverride, osDistro string) []PackageSpec { var override *PackageSpecOverride for _, o := range overrides { if o.OSDistro == osDistro { override = &o break } } if override == nil { return packageSpecs } // Remove packages in the spec that matches the overrides in // Subtractions. var out []PackageSpec subtractions := make(map[string]bool) for _, spec := range override.Subtractions { subtractions[spec.Name] = true } for _, spec := range packageSpecs { if _, ok := subtractions[spec.Name]; !ok { out = append(out, spec) } } // Add packages in the spec that matches the overrides in Additions. return append(out, override.Additions...) } // extractUpstreamVersion returns the upstream version of the given full // version in dpkg format. E.g., "1:1.0.6-2ubuntu2.1" -> "1.0.6". func extractUpstreamVersion(version string) string { // The full version is in the format of // "[epoch:]upstream_version[-debian_revision]". See // https://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Version. version = strings.Trim(version, " '") if i := strings.Index(version, ":"); i != -1 { version = version[i+1:] } if i := strings.Index(version, "-"); i != -1 { version = version[:i] } return version } // toSemVerRange converts the input to a semantic version range. // E.g., ">=1.0" -> ">=1.0.x" // ">=1" -> ">=1.x" // ">=1 <=2.3" -> ">=1.x <=2.3.x" // ">1 || >3.1.0 !4.2" -> ">1.x || >3.1.0 !4.2.x" func toSemVerRange(input string) string { var output []string fields := strings.Fields(input) for _, f := range fields { numDots, hasDigits := 0, false for _, c := range f { switch { case c == '.': numDots++ case c >= '0' && c <= '9': hasDigits = true } } if hasDigits && numDots < semVerDotsCount { f = strings.TrimRight(f, " ") f += ".x" } output = append(output, f) } return strings.Join(output, " ") } // toSemVer converts the input to a semantic version, and an empty string on // error. func toSemVer(version string) string { // Remove the first non-digit and non-dot character as well as the ones // following it. // E.g., "1.8.19p1" -> "1.8.19". if i := strings.IndexFunc(version, func(c rune) bool { if (c < '0' || c > '9') && c != '.' { return true } return false }); i != -1 { version = version[:i] } // Remove the trailing dots if there's any, and then returns an empty // string if nothing left. version = strings.TrimRight(version, ".") if version == "" { return "" } numDots := strings.Count(version, ".") switch { case numDots < semVerDotsCount: // Add minor version and patch version. // E.g. "1.18" -> "1.18.0" and "481" -> "481.0.0". version += strings.Repeat(".0", semVerDotsCount-numDots) case numDots > semVerDotsCount: // Remove anything beyond the patch version // E.g. "2.0.10.4" -> "2.0.10". for numDots != semVerDotsCount { if i := strings.LastIndex(version, "."); i != -1 { version = version[:i] numDots-- } } } // Remove leading zeros in major/minor/patch version. // E.g., "2.02" -> "2.2" // "8.0.0095" -> "8.0.95" var subs []string for _, s := range strings.Split(version, ".") { s := strings.TrimLeft(s, "0") if s == "" { s = "0" } subs = append(subs, s) } return strings.Join(subs, ".") } system-validators-1.4.0/validators/package_validator_other.go000066400000000000000000000022141401116447000245430ustar00rootroot00000000000000// +build !linux /* Copyright 2020 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package system // NOOP for non-Linux OSes. // packageValidator implements the Validator interface. It validates packages // and their versions. type packageValidator struct { reporter Reporter } // Name returns the name of the package validator. func (validator *packageValidator) Name() string { return "package" } // Validate checks packages and their versions against the packageSpecs using // the packageManager, and returns an error on any package/version mismatch. func (validator *packageValidator) Validate(spec SysSpec) ([]error, []error) { return nil, nil } system-validators-1.4.0/validators/package_validator_test.go000066400000000000000000000150631401116447000244070ustar00rootroot00000000000000// +build linux /* Copyright 2017 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package system import ( "fmt" "reflect" "testing" "github.com/pkg/errors" ) func TestExtractUpstreamVersion(t *testing.T) { for _, test := range []struct { input string expected string }{ { input: "", expected: "", }, { input: "1.0.6", expected: "1.0.6", }, { input: "1:1.0.6", expected: "1.0.6", }, { input: "1.0.6-2ubuntu2.1", expected: "1.0.6", }, { input: "1:1.0.6-2ubuntu2.1", expected: "1.0.6", }, } { t.Run(fmt.Sprintf("input:%s,expected:%s", test.input, test.expected), func(t *testing.T) { got := extractUpstreamVersion(test.input) if test.expected != got { t.Errorf("extractUpstreamVersion(%q) = %q, want %q", test.input, got, test.expected) } }) } } func TestToSemVer(t *testing.T) { for _, test := range []struct { input string expected string }{ { input: "", expected: "", }, { input: "1.2.3", expected: "1.2.3", }, { input: "1.8.19p1", expected: "1.8.19", }, { input: "1.8.19.p1", expected: "1.8.19", }, { input: "p1", expected: "", }, { input: "1.18", expected: "1.18.0", }, { input: "481", expected: "481.0.0", }, { input: "2.0.10.4", expected: "2.0.10", }, { input: "03", expected: "3.0.0", }, { input: "2.02", expected: "2.2.0", }, { input: "8.0.0095", expected: "8.0.95", }, } { t.Run(fmt.Sprintf("input:%s,expected:%s", test.input, test.expected), func(t *testing.T) { got := toSemVer(test.input) if test.expected != got { t.Errorf("toSemVer(%q) = %q, want %q", test.input, got, test.expected) } }) } } func TestToSemVerRange(t *testing.T) { for _, test := range []struct { input string expected string }{ { input: "", expected: "", }, { input: ">=1.0.0", expected: ">=1.0.0", }, { input: ">=1.0", expected: ">=1.0.x", }, { input: ">=1", expected: ">=1.x", }, { input: ">=1 || !2.3", expected: ">=1.x || !2.3.x", }, { input: ">1 || >3.1.0 !4.2", expected: ">1.x || >3.1.0 !4.2.x", }, } { t.Run(fmt.Sprintf("input:%s,expected:%s", test.input, test.expected), func(t *testing.T) { got := toSemVerRange(test.input) if test.expected != got { t.Errorf("toSemVerRange(%q) = %q, want %q", test.input, got, test.expected) } }) } } // testPackageManager implements the packageManager interface. type testPackageManager struct { packageVersions map[string]string } func (m testPackageManager) getPackageVersion(packageName string) (string, error) { if v, ok := m.packageVersions[packageName]; ok { return v, nil } return "", errors.Errorf("package %q does not exist", packageName) } func TestValidatePackageVersion(t *testing.T) { testKernelRelease := "test-kernel-release" manager := testPackageManager{ packageVersions: map[string]string{ "foo": "1.0.0", "bar": "2.1.0", "bar-" + testKernelRelease: "3.0.0", }, } v := &packageValidator{ reporter: DefaultReporter, kernelRelease: testKernelRelease, } for _, test := range []struct { desc string specs []PackageSpec errs []error }{ { desc: "all packages meet the spec", specs: []PackageSpec{ {Name: "foo", VersionRange: ">=1.0"}, {Name: "bar", VersionRange: ">=2.0 <= 3.0"}, }, }, { desc: "package version does not meet the spec", specs: []PackageSpec{ {Name: "foo", VersionRange: ">=1.0"}, {Name: "bar", VersionRange: ">=3.0"}, }, errs: []error{ errors.New("package \"bar 2.1.0\" does not meet the spec \"bar (>=3.0)\""), }, }, { desc: "package not installed", specs: []PackageSpec{ {Name: "baz"}, }, errs: []error{ errors.New("package \"baz\" does not exist"), }, }, { desc: "use variable in package name", specs: []PackageSpec{ {Name: "bar-${KERNEL_RELEASE}", VersionRange: ">=3.0"}, }, }, } { t.Run(test.desc, func(t *testing.T) { _, errs := v.validate(test.specs, manager) if len(test.errs) == 0 && len(errs) > 0 { t.Errorf("%s: v.validate(): err = %s", test.desc, errs) } if len(test.errs) > 0 { if len(errs) == 0 { t.Errorf("%s: v.validate() is expected to fail.", test.desc) } else if len(errs) != len(test.errs) { t.Errorf("%s: v.validate(): errs = %q, want = %q", test.desc, errs, test.errs) } else { for i, v := range test.errs { if v.Error() != errs[i].Error() { t.Errorf("%s: v.validate(): errs = %q, want = %q", test.desc, errs, test.errs) } } } } }) } } func TestApplyPackageOverride(t *testing.T) { for _, test := range []struct { name string overrides []PackageSpecOverride osDistro string specs []PackageSpec expected []PackageSpec }{ { name: "foo>=1.0", specs: []PackageSpec{{Name: "foo", VersionRange: ">=1.0"}}, expected: []PackageSpec{{Name: "foo", VersionRange: ">=1.0"}}, }, { name: "ubuntu:foo>=1.0/bar>=2.0", osDistro: "ubuntu", overrides: []PackageSpecOverride{ { OSDistro: "ubuntu", Subtractions: []PackageSpec{{Name: "foo"}}, Additions: []PackageSpec{{Name: "bar", VersionRange: ">=2.0"}}, }, }, specs: []PackageSpec{{Name: "foo", VersionRange: ">=1.0"}}, expected: []PackageSpec{{Name: "bar", VersionRange: ">=2.0"}}, }, { name: "ubuntu:foo>=1.0/debian:foo", osDistro: "ubuntu", overrides: []PackageSpecOverride{ { OSDistro: "debian", Subtractions: []PackageSpec{{Name: "foo"}}, }, }, specs: []PackageSpec{{Name: "foo", VersionRange: ">=1.0"}}, expected: []PackageSpec{{Name: "foo", VersionRange: ">=1.0"}}, }, } { t.Run(test.name, func(t *testing.T) { got := applyPackageSpecOverride(test.specs, test.overrides, test.osDistro) if !reflect.DeepEqual(test.expected, got) { t.Errorf("applyPackageSpecOverride(%+v, %+v, %s) = %+v, want = %+v", test.specs, test.overrides, test.osDistro, got, test.expected) } }) } } system-validators-1.4.0/validators/report.go000066400000000000000000000035411401116447000212210ustar00rootroot00000000000000/* Copyright 2016 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package system import ( "fmt" "io" "os" "github.com/pkg/errors" ) // ValidationResultType is type of the validation result. Different validation results // corresponds to different colors. type ValidationResultType int32 const ( good ValidationResultType = iota bad warn ) // color is the color of the message. type color int32 const ( red color = 31 green color = 32 yellow color = 33 white color = 37 ) func colorize(s string, c color) string { return fmt.Sprintf("\033[0;%dm%s\033[0m", c, s) } // StreamReporter is the default reporter for the system verification test. type StreamReporter struct { // The stream that this reporter is writing to WriteStream io.Writer } // Report reports validation result in different color depending on the result type. func (dr *StreamReporter) Report(key, value string, resultType ValidationResultType) error { var c color switch resultType { case good: c = green case bad: c = red case warn: c = yellow default: c = white } if dr.WriteStream == nil { return errors.New("WriteStream has to be defined for this reporter") } fmt.Fprintf(dr.WriteStream, "%s: %s\n", colorize(key, white), colorize(value, c)) return nil } // DefaultReporter is the default Reporter var DefaultReporter = &StreamReporter{ WriteStream: os.Stdout, } system-validators-1.4.0/validators/types.go000066400000000000000000000125421401116447000210530ustar00rootroot00000000000000/* Copyright 2016 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package system // KernelConfig defines one kernel configuration item. type KernelConfig struct { // Name is the general name of the kernel configuration. It is used to // match kernel configuration. Name string `json:"name,omitempty"` // TODO(yguo0905): Support the "or" operation, which will be the same // as the "aliases". // // Aliases are aliases of the kernel configuration. Some configuration // has different names in different kernel version. Names of different // versions will be treated as aliases. Aliases []string `json:"aliases,omitempty"` // Description is the description of the kernel configuration, for example: // * What is it used for? // * Why is it needed? // * Who needs it? Description string `json:"description,omitempty"` } // KernelSpec defines the specification for the kernel. Currently, it contains // specification for: // * Kernel Version // * Kernel Configuration type KernelSpec struct { // Versions define supported kernel version. It is a group of regexps. Versions []string `json:"versions,omitempty"` // Required contains all kernel configurations required to be enabled // (built in or as module). Required []KernelConfig `json:"required,omitempty"` // Optional contains all kernel configurations are required for optional // features. Optional []KernelConfig `json:"optional,omitempty"` // Forbidden contains all kernel configurations which areforbidden (disabled // or not set) Forbidden []KernelConfig `json:"forbidden,omitempty"` } // DockerSpec defines the requirement configuration for docker. Currently, it only // contains spec for graph driver. type DockerSpec struct { // Version is a group of regex matching supported docker versions. Version []string `json:"version,omitempty"` // GraphDriver is the graph drivers supported by kubelet. GraphDriver []string `json:"graphDriver,omitempty"` } // RuntimeSpec is the abstract layer for different runtimes. Different runtimes // should put their spec inside the RuntimeSpec. type RuntimeSpec struct { *DockerSpec `json:",inline"` } // PackageSpec defines the required packages and their versions. // PackageSpec is only supported on OS distro with Debian package manager. // // TODO(yguo0905): Support operator OR of multiple packages for the case where // either "foo (>=1.0)" or "bar (>=2.0)" is required. type PackageSpec struct { // Name is the name of the package to be checked. Name string `json:"name,omitempty"` // VersionRange represents a range of versions that the package must // satisfy. Note that the version requirement will not be enforced if // the version range is empty. For example, // - "" would match any versions but the package must be installed. // - ">=1" would match "1.0.0", "1.0.1", "1.1.0", and "2.0". // - ">1.0 <2.0" would match between both ranges, so "1.1.1" and "1.8.7" // but not "1.0.0" or "2.0.0". // - "<2.0.0 || >=3.0.0" would match "1.0.0" and "3.0.0" but not "2.0.0". VersionRange string `json:"versionRange,omitempty"` // Description explains the reason behind this package requirements. // // TODO(yguo0905): Print the description where necessary. Description string `json:"description,omitempty"` } // PackageSpecOverride defines the overrides on the PackageSpec for an OS // distro. type PackageSpecOverride struct { // OSDistro identifies to which OS distro this override applies. // Must be "ubuntu", "cos" or "coreos". OSDistro string `json:"osDistro,omitempty"` // Subtractions is a list of package names that are excluded from the // package spec. Subtractions []PackageSpec `json:"subtractions,omitempty"` // Additions is a list of additional package requirements included the // package spec. Additions []PackageSpec `json:"additions,omitempty"` } // SysSpec defines the requirement of supported system. Currently, it only contains // spec for OS, Kernel and Cgroups. type SysSpec struct { // OS is the operating system of the SysSpec. OS string `json:"os,omitempty"` // KernelConfig defines the spec for kernel. KernelSpec KernelSpec `json:"kernelSpec,omitempty"` // Cgroups is the required cgroups. Cgroups []string `json:"cgroups,omitempty"` // CgroupsOptional is the optional cgroups. CgroupsOptional []string `json:"cgroupsOptional,omitempty"` // CgroupsV2 is the required cgroups v2. CgroupsV2 []string `json:"cgroupsV2,omitempty"` // CgroupsV2Optional is the optional cgroups v2. CgroupsV2Optional []string `json:"cgroupsV2Optional,omitempty"` // RuntimeSpec defines the spec for runtime. RuntimeSpec RuntimeSpec `json:"runtimeSpec,omitempty"` // PackageSpec defines the required packages and their versions. PackageSpecs []PackageSpec `json:"packageSpecs,omitempty"` // PackageSpec defines the overrides of the required packages and their // versions for an OS distro. PackageSpecOverrides []PackageSpecOverride `json:"packageSpecOverrides,omitempty"` } system-validators-1.4.0/validators/types_unix.go000066400000000000000000000061601401116447000221150ustar00rootroot00000000000000// +build !windows /* Copyright 2017 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package system import ( "os/exec" "strings" ) // DefaultSysSpec is the default SysSpec for Linux var DefaultSysSpec = SysSpec{ OS: "Linux", KernelSpec: KernelSpec{ Versions: []string{`^3\.[1-9][0-9].*$`, `^([4-9]|[1-9][0-9]+)\.([0-9]+)\.([0-9]+).*$`}, // Requires 3.10+, or newer // TODO(random-liu): Add more config // TODO(random-liu): Add description for each kernel configuration: Required: []KernelConfig{ {Name: "NAMESPACES"}, {Name: "NET_NS"}, {Name: "PID_NS"}, {Name: "IPC_NS"}, {Name: "UTS_NS"}, {Name: "CGROUPS"}, {Name: "CGROUP_CPUACCT"}, {Name: "CGROUP_DEVICE"}, {Name: "CGROUP_FREEZER"}, {Name: "CGROUP_PIDS"}, {Name: "CGROUP_SCHED"}, {Name: "CPUSETS"}, {Name: "MEMCG"}, {Name: "INET"}, {Name: "EXT4_FS"}, {Name: "PROC_FS"}, {Name: "NETFILTER_XT_TARGET_REDIRECT", Aliases: []string{"IP_NF_TARGET_REDIRECT"}}, {Name: "NETFILTER_XT_MATCH_COMMENT"}, {Name: "FAIR_GROUP_SCHED"}, }, Optional: []KernelConfig{ {Name: "OVERLAY_FS", Aliases: []string{"OVERLAYFS_FS"}, Description: "Required for overlayfs."}, {Name: "AUFS_FS", Description: "Required for aufs."}, {Name: "BLK_DEV_DM", Description: "Required for devicemapper."}, {Name: "CFS_BANDWIDTH", Description: "Required for CPU quota."}, {Name: "CGROUP_HUGETLB", Description: "Required for hugetlb cgroup."}, }, Forbidden: []KernelConfig{}, }, Cgroups: []string{"cpu", "cpuacct", "cpuset", "devices", "freezer", "memory", "pids"}, CgroupsOptional: []string{ // The hugetlb cgroup is optional since some kernels are compiled without support for huge pages // and therefore lacks corresponding hugetlb cgroup "hugetlb", }, CgroupsV2: []string{"cpu", "cpuset", "devices", "freezer", "memory", "pids"}, CgroupsV2Optional: []string{"hugetlb"}, RuntimeSpec: RuntimeSpec{ DockerSpec: &DockerSpec{ Version: []string{`1\.1[1-3]\..*`, `17\.0[3,6,9]\..*`, `18\.0[6,9]\..*`, `19\.03\..*`, `20\.10\..*`}, GraphDriver: []string{"aufs", "overlay", "overlay2", "devicemapper", "zfs"}, }, }, } // KernelValidatorHelperImpl is the 'linux' implementation of KernelValidatorHelper type KernelValidatorHelperImpl struct{} var _ KernelValidatorHelper = &KernelValidatorHelperImpl{} // GetKernelReleaseVersion returns the kernel release version (ex. 4.4.0-96-generic) as a string func (o *KernelValidatorHelperImpl) GetKernelReleaseVersion() (string, error) { releaseVersion, err := exec.Command("uname", "-r").CombinedOutput() if err != nil { return "", err } return strings.TrimSpace(string(releaseVersion)), nil } system-validators-1.4.0/validators/types_windows.go000066400000000000000000000034201401116447000226200ustar00rootroot00000000000000// +build windows /* Copyright 2017 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package system import ( "os/exec" "strings" ) // DefaultSysSpec is the default SysSpec for Windows var DefaultSysSpec = SysSpec{ OS: "Microsoft Windows Server 2016", KernelSpec: KernelSpec{ Versions: []string{`10\.0\.1439[3-9]`, `10\.0\.14[4-9][0-9]{2}`, `10\.0\.1[5-9][0-9]{3}`, `10\.0\.[2-9][0-9]{4}`, `10\.[1-9]+\.[0-9]+`}, //requires >= '10.0.14393' Required: []KernelConfig{}, Optional: []KernelConfig{}, Forbidden: []KernelConfig{}, }, RuntimeSpec: RuntimeSpec{ DockerSpec: &DockerSpec{ Version: []string{`18\.0[6,9]\..*`, `19\.03\..*`}, GraphDriver: []string{"windowsfilter"}, }, }, } // KernelValidatorHelperImpl is the 'windows' implementation of KernelValidatorHelper type KernelValidatorHelperImpl struct{} var _ KernelValidatorHelper = &KernelValidatorHelperImpl{} // GetKernelReleaseVersion returns the windows release version (ex. 10.0.14393) as a string func (o *KernelValidatorHelperImpl) GetKernelReleaseVersion() (string, error) { args := []string{"(Get-CimInstance Win32_OperatingSystem).Version"} releaseVersion, err := exec.Command("powershell", args...).Output() if err != nil { return "", err } return strings.TrimSpace(string(releaseVersion)), nil } system-validators-1.4.0/validators/validators.go000066400000000000000000000043561401116447000220630ustar00rootroot00000000000000/* Copyright 2016 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package system import ( "fmt" "runtime" ) // Validator is the interface for all validators. type Validator interface { // Name is the name of the validator. Name() string // Validate is the validate function. Validate(SysSpec) ([]error, []error) } // Reporter is the interface for the reporters for the validators. type Reporter interface { // Report reports the results of the system verification Report(string, string, ValidationResultType) error } // Validate uses validators to validate the system and returns a warning or error. func Validate(spec SysSpec, validators []Validator) ([]error, []error) { var errs []error var warns []error for _, v := range validators { fmt.Printf("Validating %s...\n", v.Name()) warn, err := v.Validate(spec) if len(err) != 0 { errs = append(errs, err...) } if len(warn) != 0 { warns = append(warns, warn...) } } return warns, errs } // ValidateSpec uses all default validators to validate the system and writes to stdout. func ValidateSpec(spec SysSpec, containerRuntime string) ([]error, []error) { // OS-level validators. var osValidators = []Validator{ &OSValidator{Reporter: DefaultReporter}, &KernelValidator{Reporter: DefaultReporter}, } // Docker-specific validators. var dockerValidators = []Validator{ &DockerValidator{Reporter: DefaultReporter}, } validators := osValidators switch containerRuntime { case "docker": validators = append(validators, dockerValidators...) } // Linux-specific validators. if runtime.GOOS == "linux" { validators = append(validators, &CgroupsValidator{Reporter: DefaultReporter}, &packageValidator{reporter: DefaultReporter}, ) } return Validate(spec, validators) }