Skip to content

Commit

Permalink
Set up examples gate job for 1.11 (kubernetes#9345)
Browse files Browse the repository at this point in the history
There were things changed upstream which have blocked the examples test
from running correctly:

- PodSecurityPolicy has been MOVED from extensions API group into
  policy. All samples that are using this resource type needs a change.
- The 'GetCodecForObject()' function in testapi/testapi package was
  removed in 1.11 for unknown reasons. This change has broken our job as
  well as the test code in the kubernetes/examples project.
- Kubernetes is now using 1.10.2+ verion of Go for compilation so we
  have to change the .travis.yml accordingly.

This PR fixes the above problems.

Note also, the `TestReadme` function is **removed** for two reasons:

- It is only testing the snippets in the `volumes.md` file which is now
  broken because of the introduction of service account token injection.
- It is regarded as too aggressive/restrictive a test. We cannot
  guarantee that YAML snippets in the markdown files are complete and
  can be used as is. Instead I believe we should encourage people to use
  template snippets that contain the **key** information for a topic.
  When we see a need for a full template that can be used directly, we
  may want to extract the YAML contents into the `examples`
  subdirectory.
  • Loading branch information
tengqm authored and k8s-ci-robot committed Jul 2, 2018
1 parent d15da5a commit 59e626e
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 99 deletions.
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
language: go
go:
- 1.9.3
- 1.10.2

# Don't want default ./... here:
install:
Expand All @@ -10,7 +10,7 @@ install:
# Fetch dependencies for us to run the tests in test/examples_test.go
- go get -t -v k8s.io/website/test
# Make sure we are testing against the correct branch
- pushd $GOPATH/src/k8s.io/kubernetes && git checkout release-1.10 && popd
- pushd $GOPATH/src/k8s.io/kubernetes && git checkout release-1.11 && popd

# Simplified deduplication of dependencies.
- cp -L -R $GOPATH/src/k8s.io/kubernetes/vendor/ $GOPATH/src/
Expand Down
132 changes: 35 additions & 97 deletions test/examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import (
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strings"
"testing"

Expand All @@ -33,6 +32,7 @@ import (
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apimachinery/pkg/util/yaml"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/api/testapi"
"k8s.io/kubernetes/pkg/apis/admissionregistration"
ar_validation "k8s.io/kubernetes/pkg/apis/admissionregistration/validation"
Expand All @@ -59,6 +59,33 @@ import (
schedulerapilatest "k8s.io/kubernetes/pkg/scheduler/api/latest"
)

func getCodecForObject(obj runtime.Object) (runtime.Codec, error) {
kinds, _, err := legacyscheme.Scheme.ObjectKinds(obj)
if err != nil {
return nil, fmt.Errorf("unexpected encoding error: %v", err)
}
kind := kinds[0]

for _, group := range testapi.Groups {
if group.GroupVersion().Group != kind.Group {
continue
}

if legacyscheme.Scheme.Recognizes(kind) {
return group.Codec(), nil
}
}
// Codec used for unversioned types
if legacyscheme.Scheme.Recognizes(kind) {
serializer, ok := runtime.SerializerInfoForMediaType(legacyscheme.Codecs.SupportedMediaTypes(), runtime.ContentTypeJSON)
if !ok {
return nil, fmt.Errorf("no serializer registered for json")
}
return serializer.Serializer, nil
}
return nil, fmt.Errorf("unexpected kind: %v", kind)
}

func validateObject(obj runtime.Object) (errors field.ErrorList) {
// Enable CustomPodDNS for testing
utilfeature.DefaultFeatureGate.Set("CustomPodDNS=true")
Expand Down Expand Up @@ -173,8 +200,8 @@ func validateObject(obj runtime.Object) (errors field.ErrorList) {
t.Namespace = api.NamespaceDefault
}
errors = ext_validation.ValidateIngress(t)
case *extensions.PodSecurityPolicy:
errors = ext_validation.ValidatePodSecurityPolicy(t)
case *policy.PodSecurityPolicy:
errors = policy_validation.ValidatePodSecurityPolicy(t)
case *extensions.ReplicaSet:
if t.Namespace == "" {
t.Namespace = api.NamespaceDefault
Expand Down Expand Up @@ -289,9 +316,9 @@ func TestExampleObjectSchemas(t *testing.T) {
"nginx-deployment": {&extensions.Deployment{}},
},
"docs/concepts/policy": {
"privileged-psp": {&extensions.PodSecurityPolicy{}},
"restricted-psp": {&extensions.PodSecurityPolicy{}},
"example-psp": {&extensions.PodSecurityPolicy{}},
"privileged-psp": {&policy.PodSecurityPolicy{}},
"restricted-psp": {&policy.PodSecurityPolicy{}},
"example-psp": {&policy.PodSecurityPolicy{}},
},
"docs/concepts/services-networking": {
"curlpod": {&extensions.Deployment{}},
Expand All @@ -308,8 +335,8 @@ func TestExampleObjectSchemas(t *testing.T) {
"frontend": {&extensions.ReplicaSet{}},
"hpa-rs": {&autoscaling.HorizontalPodAutoscaler{}},
"job": {&batch.Job{}},
"my-repset": {&extensions.ReplicaSet{}},
"nginx-deployment": {&extensions.Deployment{}},
"my-repset": {&extensions.ReplicaSet{}},
"replication": {&api.ReplicationController{}},
},
"docs/tasks/access-application-cluster": {
Expand Down Expand Up @@ -540,7 +567,7 @@ func TestExampleObjectSchemas(t *testing.T) {
// &schedulerapi.Policy, and remove this
// special case
} else {
codec, err := testapi.GetCodecForObject(expectedType)
codec, err := getCodecForObject(expectedType)
if err != nil {
t.Errorf("Could not get codec for %s: %s", expectedType, err)
}
Expand All @@ -562,92 +589,3 @@ func TestExampleObjectSchemas(t *testing.T) {
}
}
}

// This regex is tricky, but it works. For future me, here is the decode:
//
// Flags: (?ms) = multiline match, allow . to match \n
// 1) Look for a line that starts with ``` (a markdown code block)
// 2) (?: ... ) = non-capturing group
// 3) (P<name>) = capture group as "name"
// 4) Look for #1 followed by either:
// 4a) "yaml" followed by any word-characters followed by a newline (e.g. ```yamlfoo\n)
// 4b) "any word-characters followed by a newline (e.g. ```json\n)
// 5) Look for either:
// 5a) #4a followed by one or more characters (non-greedy)
// 5b) #4b followed by { followed by one or more characters (non-greedy) followed by }
// 6) Look for #5 followed by a newline followed by ``` (end of the code block)
//
// This could probably be simplified, but is already too delicate. Before any
// real changes, we should have a test case that just tests this regex.
var sampleRegexp = regexp.MustCompile("(?ms)^```(?:(?P<type>yaml)\\w*\\n(?P<content>.+?)|\\w*\\n(?P<content>\\{.+?\\}))\\n^```")
var subsetRegexp = regexp.MustCompile("(?ms)\\.{3}")

// Validates examples embedded in Markdown files.
func TestReadme(t *testing.T) {
// BlockVolume required for local volume example
utilfeature.DefaultFeatureGate.Set("BlockVolume=true")

paths := []struct {
file string
expectedType []runtime.Object // List of all valid types for the whole doc
}{
{"../content/en/docs/concepts/storage/volumes.md", []runtime.Object{
&api.Pod{},
&api.PersistentVolume{},
}},
}

for _, path := range paths {
data, err := ioutil.ReadFile(path.file)
if err != nil {
t.Errorf("Unable to read file %s: %v", path, err)
continue
}

matches := sampleRegexp.FindAllStringSubmatch(string(data), -1)
if matches == nil {
continue
}
for _, match := range matches {
var content, subtype string
for i, name := range sampleRegexp.SubexpNames() {
if name == "type" {
subtype = match[i]
}
if name == "content" && match[i] != "" {
content = match[i]
}
}
if subtype == "yaml" && subsetRegexp.FindString(content) != "" {
t.Logf("skipping (%s): \n%s", subtype, content)
continue
}

json, err := yaml.ToJSON([]byte(content))
if err != nil {
t.Errorf("%s could not be converted to JSON: %v\n%s", path, err, string(content))
}

var expectedType runtime.Object
for _, expectedType = range path.expectedType {
err = runtime.DecodeInto(testapi.Default.Codec(), json, expectedType)
if err == nil {
break
}
}
if err != nil {
t.Errorf("%s did not decode correctly: %v\n%s", path, err, string(content))
continue
}

if errors := validateObject(expectedType); len(errors) > 0 {
t.Errorf("%s did not validate correctly: %v", path, errors)
}
_, err = runtime.Encode(testapi.Default.Codec(), expectedType)
if err != nil {
t.Errorf("Could not encode object: %v", err)
continue
}
}
}
}

0 comments on commit 59e626e

Please sign in to comment.