Skip to content

Commit

Permalink
Migrate kepval to this repo
Browse files Browse the repository at this point in the history
Signed-off-by: chuckha <ha.chuck@gmail.com>
  • Loading branch information
chuckha committed Jul 30, 2019
1 parent b636feb commit 64a2a43
Show file tree
Hide file tree
Showing 10 changed files with 680 additions and 23 deletions.
54 changes: 54 additions & 0 deletions cmd/kepval/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
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 (
"flag"
"fmt"
"os"

"k8s.io/enhancements/pkg/kepval/keps"
)

func main() {
list := flag.NewFlagSet("list", flag.ExitOnError)
list.Parse(os.Args[1:])

parser := &keps.Parser{}
exit := 0
for _, filename := range list.Args() {
file, err := os.Open(filename)
if err != nil {
fmt.Printf("could not open file: %v", err)
os.Exit(1)
}
defer file.Close()
kep := parser.Parse(file)
// if error is nil we can move on
if kep.Error == nil {
continue
}

fmt.Printf("%v has an error: %q\n", filename, kep.Error.Error())
exit = 1
}

if exit == 0 {
fmt.Println("No validation errors")
}
os.Exit(exit)
}
84 changes: 84 additions & 0 deletions cmd/kepval/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
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 (
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"

"github.com/pkg/errors"
)

func TestIntegration(t *testing.T) {
tempDir, err := ioutil.TempDir(".", "test")
if err != nil {
t.Fatalf("%+v", errors.WithStack(err))
}
defer os.RemoveAll(tempDir)
fmt.Println("Cloning...")
cmd := exec.Command("git", "clone", "https://github.com/kubernetes/enhancements")
cmd.Dir = tempDir
out, err := cmd.CombinedOutput()
if err != nil {
fmt.Println(string(out))
t.Fatalf("%+v", errors.WithStack(err))
}
fmt.Println("Building...")
cmd = exec.Command("go", "build", "-o", filepath.Join(tempDir, "kepval"), ".")
if out, err := cmd.CombinedOutput(); err != nil {
fmt.Println(string(out))
t.Fatal(err)
}
fmt.Println("Walking...")
if filepath.Walk(
filepath.Join(tempDir, "enhancements", "keps"),
func(path string, info os.FileInfo, err error) error {
if info.IsDir() {
return nil
}
if err != nil {
t.Fatalf("%+v", err)
}
if ignore(info.Name()) {
return nil
}
cmd = exec.Command(filepath.Join(tempDir, "kepval"), path)
if out, err := cmd.CombinedOutput(); err != nil {
fmt.Println(string(out))
t.Fatal(err)
}
return nil
},
) != nil {
t.Fatal(err)
}
}

func ignore(name string) bool {
if !strings.HasSuffix(name, "md") {
return true
}
if name == "0023-documentation-for-images.md" {
return true
}
return false
}
14 changes: 14 additions & 0 deletions docs/kepval.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# kepval

`kepval` is a tool that checks the YAML metadata in a KEP (Kubernetes
Enhancement Proposal) is valid.

## Getting started

1. Install `kepval`: `GO111MODULE=on go get k8s.io/enhancements/cmd/kepval`
2. [optional] clone the enhancements for test data `git clone https://github.com/kubernetes/enhancements.git`
3. Run `kepval <path to kep.md>`

## Development

1. Run the tests with `go test -cover ./...`
8 changes: 8 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module k8s.io/enhancements

go 1.12

require (
github.com/pkg/errors v0.8.1
gopkg.in/yaml.v2 v2.2.2
)
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
25 changes: 2 additions & 23 deletions hack/verify-kep-metadata.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,9 @@ set -o errexit
set -o nounset
set -o pipefail

TOOL_VERSION=v0.1.0

# cd to the root path
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)"
cd "${ROOT}"

# create a temporary directory
TMP_DIR=$(mktemp -d)

# cleanup
exitHandler() (
echo "Cleaning up..."
rm -rf "${TMP_DIR}"
)
trap exitHandler EXIT

# perform go get in a temp dir as we are not tracking this version in a go module
# if we do the go get in the repo, it will create / update a go.mod and go.sum
cd "${TMP_DIR}"
GO111MODULE=on GOBIN="${TMP_DIR}" go get "github.com/chuckha/kepview/cmd/kepval@${TOOL_VERSION}"
export PATH="${TMP_DIR}:${PATH}"
cd "${ROOT}"

echo "Checking metadata validity..."
# * ignore "0023-documentation-for-images.md" because it is not a real KEP
# * ignore "YYYYMMDD-kep-template.md" because it is not a real KEP
grep --recursive --files-with-matches --regexp '---' --include='*.md' keps | grep --invert-match "YYYYMMDD-kep-template.md" | grep --invert-match "0023-documentation-for-images.md" | xargs kepval
# run the tests
GO111MODULE=on go test ./cmd/kepval/...
98 changes: 98 additions & 0 deletions pkg/kepval/keps/proposals.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
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 keps

import (
"bufio"
"bytes"
"io"
"strings"

"github.com/pkg/errors"
"gopkg.in/yaml.v2"
"k8s.io/enhancements/pkg/kepval/keps/validations"
)

type Proposals []*Proposal

func (p *Proposals) AddProposal(proposal *Proposal) {
*p = append(*p, proposal)
}

type Proposal struct {
Title string `yaml:"title"`
Authors []string `yaml:,flow`
OwningSIG string `yaml:"owning-sig"`
ParticipatingSIGs []string `yaml:"participating-sigs",flow,omitempty`
Reviewers []string `yaml:,flow`
Approvers []string `yaml:,flow`
Editor string `yaml:"editor,omitempty"`
CreationDate string `yaml:"creation-date"`
LastUpdated string `yaml:"last-updated"`
Status string `yaml:"status"`
SeeAlso []string `yaml:"see-also,omitempty"`
Replaces []string `yaml:"replaces,omitempty"`
SupersededBy []string `yaml:"superseded-by,omitempty"`

Filename string `yaml:"-"`
Error error `yaml:"-"`
Contents string `yaml:"-"`
}

type Parser struct{}

func (p *Parser) Parse(in io.Reader) *Proposal {
scanner := bufio.NewScanner(in)
count := 0
metadata := []byte{}
var body bytes.Buffer
for scanner.Scan() {
line := scanner.Text() + "\n"
body.WriteString(line)
if count == 2 {
continue
}
if strings.Contains(line, "---") {
count++
continue
}
if count == 1 {
metadata = append(metadata, []byte(line)...)
}
}
proposal := &Proposal{
Contents: body.String(),
}
if err := scanner.Err(); err != nil {
proposal.Error = errors.Wrap(err, "error reading file")
return proposal
}

// First do structural checks
test := map[interface{}]interface{}{}
if err := yaml.Unmarshal(metadata, test); err != nil {
proposal.Error = errors.Wrap(err, "error unmarshaling YAML")
return proposal
}
if err := validations.ValidateStructure(test); err != nil {
proposal.Error = errors.Wrap(err, "error validating KEP metadata")
return proposal
}

proposal.Error = yaml.Unmarshal(metadata, proposal)
return proposal
}
71 changes: 71 additions & 0 deletions pkg/kepval/keps/proposals_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
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 keps_test

import (
"strings"
"testing"

"k8s.io/enhancements/pkg/kepval/keps"
)

func TestValidParsing(t *testing.T) {
testcases := []struct {
name string
fileContents string
}{
{
"simple test",
`---
title: test
authors:
- "@jpbetz"
- "@roycaihw"
- "@sttts"
owning-sig: sig-api-machinery
participating-sigs:
- sig-api-machinery
- sig-architecture
reviewers:
- "@deads2k"
- "@lavalamp"
- "@liggitt"
- "@mbohlool"
- "@sttts"
approvers:
- "@deads2k"
- "@lavalamp"
creation-date: 2018-04-15
last-updated: 2018-04-24
status: provisional
---`,
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
p := &keps.Parser{}
contents := strings.NewReader(tc.fileContents)
out := p.Parse(contents)
if out.Error != nil {
t.Fatalf("expected no error but got one: %v", out.Error)
}
if out == nil {
t.Fatal("out should not be nil")
}
})
}
}
Loading

0 comments on commit 64a2a43

Please sign in to comment.