github.com/abayer/test-infra@v0.0.5/mungegithub/mungers/e2e/e2e_test.go (about)

     1  /*
     2  Copyright 2015 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package e2e
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/json"
    22  	"fmt"
    23  	"net/http"
    24  	"net/http/httptest"
    25  	"reflect"
    26  	"strconv"
    27  	"testing"
    28  
    29  	"strings"
    30  
    31  	"k8s.io/contrib/test-utils/utils"
    32  	"k8s.io/test-infra/mungegithub/options"
    33  )
    34  
    35  type testHandler struct {
    36  	handler func(http.ResponseWriter, *http.Request)
    37  }
    38  
    39  func (t *testHandler) ServeHTTP(res http.ResponseWriter, req *http.Request) {
    40  	t.handler(res, req)
    41  }
    42  
    43  func marshalOrDie(obj interface{}, t *testing.T) []byte {
    44  	data, err := json.Marshal(obj)
    45  	if err != nil {
    46  		t.Errorf("unexpected error: %v", err)
    47  	}
    48  	return data
    49  }
    50  
    51  func genMockGCSListResponse(files ...string) []byte {
    52  	respTemplate := "{\"items\":[%s]}"
    53  	itemTemplate := "{\"name\":\"%s\"}"
    54  	items := []string{}
    55  	for _, file := range files {
    56  		items = append(items, fmt.Sprintf(itemTemplate, file))
    57  	}
    58  	return []byte(fmt.Sprintf(respTemplate, strings.Join(items, ",")))
    59  }
    60  
    61  func TestCheckGCSBuilds(t *testing.T) {
    62  	latestBuildNumberFoo := 42
    63  	latestBuildNumberBar := 44
    64  	latestBuildNumberBaz := 99
    65  	tests := []struct {
    66  		paths             map[string][]byte
    67  		expectedLastBuild int
    68  		expectedStatus    map[string]BuildInfo
    69  	}{
    70  		{
    71  			paths: map[string][]byte{
    72  				"/bucket/logs/foo/latest-build.txt": []byte(strconv.Itoa(latestBuildNumberFoo)),
    73  				fmt.Sprintf("/bucket/logs/foo/%v/finished.json", latestBuildNumberFoo): marshalOrDie(utils.FinishedFile{
    74  					Result:    "SUCCESS",
    75  					Timestamp: 1234,
    76  				}, t),
    77  				"/bucket/logs/bar/latest-build.txt": []byte(strconv.Itoa(latestBuildNumberBar)),
    78  				fmt.Sprintf("/bucket/logs/bar/%v/finished.json", latestBuildNumberBar): marshalOrDie(utils.FinishedFile{
    79  					Result:    "SUCCESS",
    80  					Timestamp: 1234,
    81  				}, t),
    82  				"/bucket/logs/baz/latest-build.txt": []byte(strconv.Itoa(latestBuildNumberBaz)),
    83  				fmt.Sprintf("/bucket/logs/baz/%v/finished.json", latestBuildNumberBaz): marshalOrDie(utils.FinishedFile{
    84  					Result:    "UNSTABLE",
    85  					Timestamp: 1234,
    86  				}, t),
    87  				"/storage/v1/b/bucket/o": genMockGCSListResponse(),
    88  			},
    89  			expectedStatus: map[string]BuildInfo{
    90  				"foo": {Status: "[nonblocking] Stable", ID: "42"},
    91  				"bar": {Status: "[nonblocking] Stable", ID: "44"},
    92  				"baz": {Status: "[nonblocking] Not Stable", ID: "99"},
    93  			},
    94  		},
    95  		{
    96  			paths: map[string][]byte{
    97  				"/bucket/logs/foo/latest-build.txt": []byte(strconv.Itoa(latestBuildNumberFoo)),
    98  				fmt.Sprintf("/bucket/logs/foo/%v/finished.json", latestBuildNumberFoo): marshalOrDie(utils.FinishedFile{
    99  					Result:    "SUCCESS",
   100  					Timestamp: 1234,
   101  				}, t),
   102  				"/bucket/logs/bar/latest-build.txt": []byte(strconv.Itoa(latestBuildNumberBar)),
   103  				fmt.Sprintf("/bucket/logs/bar/%v/finished.json", latestBuildNumberBar): marshalOrDie(utils.FinishedFile{
   104  					Result:    "UNSTABLE",
   105  					Timestamp: 1234,
   106  				}, t),
   107  				"/bucket/logs/baz/latest-build.txt": []byte(strconv.Itoa(latestBuildNumberBaz)),
   108  				fmt.Sprintf("/bucket/logs/baz/%v/finished.json", latestBuildNumberBaz): marshalOrDie(utils.FinishedFile{
   109  					Result:    "SUCCESS",
   110  					Timestamp: 1234,
   111  				}, t),
   112  				"/storage/v1/b/bucket/o": genMockGCSListResponse(),
   113  			},
   114  			expectedStatus: map[string]BuildInfo{
   115  				"foo": {Status: "[nonblocking] Stable", ID: "42"},
   116  				"bar": {Status: "[nonblocking] Not Stable", ID: "44"},
   117  				"baz": {Status: "[nonblocking] Stable", ID: "99"},
   118  			},
   119  		},
   120  		{
   121  			paths: map[string][]byte{
   122  				"/bucket/logs/foo/latest-build.txt": []byte(strconv.Itoa(latestBuildNumberFoo)),
   123  				fmt.Sprintf("/bucket/logs/foo/%v/finished.json", latestBuildNumberFoo): marshalOrDie(utils.FinishedFile{
   124  					Result:    "SUCCESS",
   125  					Timestamp: 1234,
   126  				}, t),
   127  				"/bucket/logs/bar/latest-build.txt": []byte(strconv.Itoa(latestBuildNumberBar)),
   128  				fmt.Sprintf("/bucket/logs/bar/%v/finished.json", latestBuildNumberBar): marshalOrDie(utils.FinishedFile{
   129  					Result:    "UNSTABLE",
   130  					Timestamp: 1234,
   131  				}, t),
   132  				fmt.Sprintf("/bucket/logs/bar/%v/artifacts/junit_01.xml", latestBuildNumberBar-1): getJUnit(5, 0),
   133  				fmt.Sprintf("/bucket/logs/bar/%v/artifacts/junit_02.xml", latestBuildNumberBar-1): getRealJUnitFailure(),
   134  				fmt.Sprintf("/bucket/logs/bar/%v/artifacts/junit_03.xml", latestBuildNumberBar-1): getJUnit(5, 0),
   135  				fmt.Sprintf("/bucket/logs/bar/%v/artifacts/junit_01.xml", latestBuildNumberBar):   getJUnit(5, 0),
   136  				fmt.Sprintf("/bucket/logs/bar/%v/artifacts/junit_02.xml", latestBuildNumberBar):   getRealJUnitFailure(),
   137  				fmt.Sprintf("/bucket/logs/bar/%v/artifacts/junit_03.xml", latestBuildNumberBar):   getJUnit(5, 0),
   138  				fmt.Sprintf("/bucket/logs/bar/%v/finished.json", latestBuildNumberBar-1): marshalOrDie(utils.FinishedFile{
   139  					Result:    "UNSTABLE",
   140  					Timestamp: 999,
   141  				}, t),
   142  				"/storage/v1/b/bucket/o": genMockGCSListResponse(
   143  					fmt.Sprintf("/bucket/logs/bar/%v/artifacts/junit_01.xml", latestBuildNumberBar-1),
   144  					fmt.Sprintf("/bucket/logs/bar/%v/artifacts/junit_02.xml", latestBuildNumberBar-1),
   145  					fmt.Sprintf("/bucket/logs/bar/%v/artifacts/junit_03.xml", latestBuildNumberBar-1),
   146  					fmt.Sprintf("/bucket/logs/bar/%v/artifacts/junit_01.xml", latestBuildNumberBar),
   147  					fmt.Sprintf("/bucket/logs/bar/%v/artifacts/junit_02.xml", latestBuildNumberBar),
   148  					fmt.Sprintf("/bucket/logs/bar/%v/artifacts/junit_03.xml", latestBuildNumberBar),
   149  				),
   150  			},
   151  			expectedStatus: map[string]BuildInfo{
   152  				"foo": {Status: "[nonblocking] Stable", ID: "42"},
   153  				"bar": {Status: "[nonblocking] Not Stable", ID: "44"},
   154  				"baz": {Status: "[nonblocking] Not Stable", ID: "-1"},
   155  			},
   156  		},
   157  
   158  		{
   159  			paths: map[string][]byte{
   160  				"/bucket/logs/foo/latest-build.txt": []byte(strconv.Itoa(latestBuildNumberFoo)),
   161  				fmt.Sprintf("/bucket/logs/foo/%v/finished.json", latestBuildNumberFoo): marshalOrDie(utils.FinishedFile{
   162  					Result:    "SUCCESS",
   163  					Timestamp: 1234,
   164  				}, t),
   165  				"/bucket/logs/bar/latest-build.txt": []byte(strconv.Itoa(latestBuildNumberBar)),
   166  				fmt.Sprintf("/bucket/logs/bar/%v/finished.json", latestBuildNumberBar): marshalOrDie(utils.FinishedFile{
   167  					Result:    "FAILURE",
   168  					Timestamp: 1234,
   169  				}, t),
   170  				"/storage/v1/b/bucket/o": genMockGCSListResponse(),
   171  			},
   172  			expectedStatus: map[string]BuildInfo{
   173  				"foo": {Status: "[nonblocking] Stable", ID: "42"},
   174  				"bar": {Status: "[nonblocking] Not Stable", ID: "44"},
   175  				"baz": {Status: "[nonblocking] Not Stable", ID: "-1"},
   176  			},
   177  		},
   178  		{
   179  			paths: map[string][]byte{
   180  				"/bucket/logs/foo/latest-build.txt": []byte(strconv.Itoa(latestBuildNumberFoo)),
   181  				fmt.Sprintf("/bucket/logs/foo/%v/finished.json", latestBuildNumberFoo): marshalOrDie(utils.FinishedFile{
   182  					Result:    "FAILURE",
   183  					Timestamp: 1234,
   184  				}, t),
   185  				"/bucket/logs/bar/latest-build.txt": []byte(strconv.Itoa(latestBuildNumberBar)),
   186  				fmt.Sprintf("/bucket/logs/bar/%v/finished.json", latestBuildNumberBar): marshalOrDie(utils.FinishedFile{
   187  					Result:    "UNSTABLE",
   188  					Timestamp: 1234,
   189  				}, t),
   190  				"/storage/v1/b/bucket/o": genMockGCSListResponse(),
   191  			},
   192  			expectedStatus: map[string]BuildInfo{
   193  				"foo": {Status: "[nonblocking] Not Stable", ID: "42"},
   194  				"bar": {Status: "[nonblocking] Not Stable", ID: "44"},
   195  				"baz": {Status: "[nonblocking] Not Stable", ID: "-1"},
   196  			},
   197  		},
   198  	}
   199  	for index, test := range tests {
   200  		server := httptest.NewServer(&testHandler{
   201  			handler: func(res http.ResponseWriter, req *http.Request) {
   202  				data, found := test.paths[req.URL.Path]
   203  				if !found {
   204  					res.WriteHeader(http.StatusNotFound)
   205  					fmt.Fprintf(res, "Unknown path: %s", req.URL.Path)
   206  					return
   207  				}
   208  				res.WriteHeader(http.StatusOK)
   209  				res.Write(data)
   210  			},
   211  		})
   212  		jobs := []string{"foo", "bar", "baz"}
   213  		e2e := &RealE2ETester{
   214  			Opts:                 options.New(),
   215  			NonBlockingJobNames:  &jobs,
   216  			BuildStatus:          map[string]BuildInfo{},
   217  			GoogleGCSBucketUtils: utils.NewTestUtils("bucket", "logs", server.URL),
   218  		}
   219  		e2e.Init(nil)
   220  
   221  		e2e.LoadNonBlockingStatus()
   222  		if !reflect.DeepEqual(test.expectedStatus, e2e.BuildStatus) {
   223  			t.Errorf("%v: expected: %v, saw: %v", index, test.expectedStatus, e2e.BuildStatus)
   224  		}
   225  	}
   226  }
   227  
   228  func getJUnit(testsNo int, failuresNo int) []byte {
   229  	return []byte(fmt.Sprintf("%v\n<testsuite tests=\"%v\" failures=\"%v\" time=\"1234\">\n</testsuite>",
   230  		ExpectedXMLHeader, testsNo, failuresNo))
   231  }
   232  
   233  func getOtherRealJUnitFailure() []byte {
   234  	return []byte(`<testsuite tests="7" failures="1" time="275.882258919">
   235  <testcase name="[k8s.io] ResourceQuota should create a ResourceQuota and capture the life of a loadBalancer service." classname="Kubernetes e2e suite" time="17.759834805"/>
   236  <testcase name="[k8s.io] ResourceQuota should create a ResourceQuota and capture the life of a secret." classname="Kubernetes e2e suite" time="21.201547548"/>
   237  <testcase name="OTHER [k8s.io] Kubectl client [k8s.io] Kubectl patch should add annotations for pods in rc [Conformance]" classname="Kubernetes e2e suite" time="126.756441938">
   238  <failure type="Failure">
   239  /go/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes/test/e2e/kubectl.go:972 May 18 13:02:24.715: No pods matched the filter.
   240  </failure>
   241  </testcase>
   242  <testcase name="[k8s.io] hostPath should give a volume the correct mode [Conformance]" classname="Kubernetes e2e suite" time="9.246191421"/>
   243  <testcase name="[k8s.io] Volumes [Feature:Volumes] [k8s.io] Ceph RBD should be mountable" classname="Kubernetes e2e suite" time="0">
   244  <skipped/>
   245  </testcase>
   246  <testcase name="[k8s.io] Deployment deployment should label adopted RSs and pods" classname="Kubernetes e2e suite" time="16.557498555"/>
   247  <testcase name="[k8s.io] ConfigMap should be consumable from pods in volume as non-root with FSGroup [Feature:FSGroup]" classname="Kubernetes e2e suite" time="0">
   248  <skipped/>
   249  </testcase>
   250  <testcase name="[k8s.io] V1Job should scale a job down" classname="Kubernetes e2e suite" time="77.122626914"/>
   251  <testcase name="[k8s.io] EmptyDir volumes volume on default medium should have the correct mode [Conformance]" classname="Kubernetes e2e suite" time="7.169679079"/>
   252  <testcase name="[k8s.io] Reboot [Disruptive] [Feature:Reboot] each node by ordering unclean reboot and ensure they function upon restart" classname="Kubernetes e2e suite" time="0">
   253  <skipped/>
   254  </testcase>
   255  </testsuite>`)
   256  }
   257  
   258  func getRealJUnitFailure() []byte {
   259  	return []byte(`<testsuite tests="7" failures="1" time="275.882258919">
   260  <testcase name="[k8s.io] ResourceQuota should create a ResourceQuota and capture the life of a loadBalancer service." classname="Kubernetes e2e suite" time="17.759834805"/>
   261  <testcase name="[k8s.io] ResourceQuota should create a ResourceQuota and capture the life of a secret." classname="Kubernetes e2e suite" time="21.201547548"/>
   262  <testcase name="[k8s.io] Kubectl client [k8s.io] Kubectl patch should add annotations for pods in rc [Conformance]" classname="Kubernetes e2e suite" time="126.756441938">
   263  <failure type="Failure">
   264  /go/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes/test/e2e/kubectl.go:972 May 18 13:02:24.715: No pods matched the filter.
   265  </failure>
   266  </testcase>
   267  <testcase name="[k8s.io] hostPath should give a volume the correct mode [Conformance]" classname="Kubernetes e2e suite" time="9.246191421"/>
   268  <testcase name="[k8s.io] Volumes [Feature:Volumes] [k8s.io] Ceph RBD should be mountable" classname="Kubernetes e2e suite" time="0">
   269  <skipped/>
   270  </testcase>
   271  <testcase name="[k8s.io] Deployment deployment should label adopted RSs and pods" classname="Kubernetes e2e suite" time="16.557498555"/>
   272  <testcase name="[k8s.io] ConfigMap should be consumable from pods in volume as non-root with FSGroup [Feature:FSGroup]" classname="Kubernetes e2e suite" time="0">
   273  <skipped/>
   274  </testcase>
   275  <testcase name="[k8s.io] V1Job should scale a job down" classname="Kubernetes e2e suite" time="77.122626914"/>
   276  <testcase name="[k8s.io] EmptyDir volumes volume on default medium should have the correct mode [Conformance]" classname="Kubernetes e2e suite" time="7.169679079"/>
   277  <testcase name="[k8s.io] Reboot [Disruptive] [Feature:Reboot] each node by ordering unclean reboot and ensure they function upon restart" classname="Kubernetes e2e suite" time="0">
   278  <skipped/>
   279  </testcase>
   280  </testsuite>`)
   281  }
   282  
   283  func getRealJUnitFailureWithTestSuitesTag() []byte {
   284  	return []byte(`<?xml version="1.0" encoding="UTF-8"?>
   285  <testsuites>
   286  	<testsuite tests="52" failures="2" time="374.434" name="k8s.io/kubernetes/test/integration">
   287  		<properties>
   288  			<property name="go.version" value="go1.6.2"></property>
   289  		</properties>
   290  		<testcase classname="integration" name="TestMasterProcessMetrics" time="0.070"></testcase>
   291  		<testcase classname="integration" name="TestApiserverMetrics" time="0.070"></testcase>
   292  		<testcase classname="integration" name="TestMasterExportsSymbols" time="0.000"></testcase>
   293  		<testcase classname="integration" name="TestPersistentVolumeRecycler" time="20.460"></testcase>
   294  		<testcase classname="integration" name="TestPersistentVolumeMultiPVs" time="10.240">
   295  			<failure message="Failed" type="">persistent_volumes_test.go:254: volumes created&#xA;persistent_volumes_test.go:260: claim created&#xA;persistent_volumes_test.go:264: volume bound&#xA;persistent_volumes_test.go:266: claim bound&#xA;persistent_volumes_test.go:284: Bind mismatch! Expected pvc-2 capacity 50000000000 but got fake-pvc-72 capacity 5000000000</failure>
   296  		</testcase>
   297  		<testcase classname="integration" name="TestPersistentVolumeMultiPVsPVCs" time="3.370">
   298  			<failure message="Failed" type="">persistent_volumes_test.go:379: PVC &#34;pvc-0&#34; is not bound</failure>
   299  		</testcase>
   300  		<testcase classname="integration" name="TestPersistentVolumeMultiPVsDiffAccessModes" time="10.110"></testcase>
   301  	</testsuite>
   302  </testsuites>
   303  `)
   304  }
   305  
   306  func TestJUnitFailureParse(t *testing.T) {
   307  	//parse junit xml result with <testsuite> as top tag
   308  	junitFailReader := bytes.NewReader(getRealJUnitFailure())
   309  	got, err := getJUnitFailures(junitFailReader)
   310  	if err != nil {
   311  		t.Fatalf("Parse error? %v", err)
   312  	}
   313  	if e, a := map[string]string{
   314  		"[k8s.io] Kubectl client [k8s.io] Kubectl patch should add annotations for pods in rc [Conformance] {Kubernetes e2e suite}": `
   315  /go/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes/test/e2e/kubectl.go:972 May 18 13:02:24.715: No pods matched the filter.
   316  `,
   317  	}, got; !reflect.DeepEqual(e, a) {
   318  		t.Errorf("Expected %v, got %v", e, a)
   319  	}
   320  
   321  	//parse junit xml result with <testsuites> as top tag
   322  	junitFailReader = bytes.NewReader(getRealJUnitFailureWithTestSuitesTag())
   323  	got, err = getJUnitFailures(junitFailReader)
   324  	if err != nil {
   325  		t.Fatalf("Parse error? %v", err)
   326  	}
   327  	if e, a := map[string]string{
   328  		"TestPersistentVolumeMultiPVs {integration}":     "persistent_volumes_test.go:254: volumes created\npersistent_volumes_test.go:260: claim created\npersistent_volumes_test.go:264: volume bound\npersistent_volumes_test.go:266: claim bound\npersistent_volumes_test.go:284: Bind mismatch! Expected pvc-2 capacity 50000000000 but got fake-pvc-72 capacity 5000000000",
   329  		"TestPersistentVolumeMultiPVsPVCs {integration}": `persistent_volumes_test.go:379: PVC "pvc-0" is not bound`,
   330  	}, got; !reflect.DeepEqual(e, a) {
   331  		t.Errorf("Expected %v, got %v", e, a)
   332  	}
   333  }