Skip to content

Commit

Permalink
Add bookinfo traffic shifting tests (istio#4390)
Browse files Browse the repository at this point in the history
Automatic merge from submit-queue.

Add bookinfo traffic shifting tests

The tests test shifting traffic for a normal user gradually from v1 to v2, first 10% is shifted to v2, then 20%  are shifted to v2.

Added html for the traffic of normal user to v2.
  • Loading branch information
vadimeisenbergibm authored and istio-merge-robot committed Mar 27, 2018
1 parent e609c35 commit 6403d79
Show file tree
Hide file tree
Showing 4 changed files with 291 additions and 19 deletions.
29 changes: 29 additions & 0 deletions samples/bookinfo/istio.io_tutorial/route-rule-reviews-80-20.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Copyright 2017,2018 Istio 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.

apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
name: reviews-80-20
spec:
destination:
name: reviews
precedence: 4
route:
- labels:
version: v1
weight: 80
- labels:
version: v2
weight: 20
29 changes: 29 additions & 0 deletions samples/bookinfo/istio.io_tutorial/route-rule-reviews-90-10.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Copyright 2017,2018 Istio 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.

apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
name: reviews-90-10
spec:
destination:
name: reviews
precedence: 4
route:
- labels:
version: v1
weight: 90
- labels:
version: v2
weight: 10
178 changes: 178 additions & 0 deletions tests/apps/bookinfo/output/productpage-normal-user-v2.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
<!DOCTYPE html>
<html>
<head>
<title>Simple Bookstore App</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">

<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">

<!-- Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css">

</head>
<body>



<nav class="navbar navbar-inverse navbar-static-top">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="#">BookInfo Sample</a>
</div>

<p class="navbar-text navbar-right">
<i class="glyphicon glyphicon-user" aria-hidden="true"></i>
<span style="padding-left: 5px;">normal-user ( <a href="logout">sign out</a> )</span>
</p>

</div>
</nav>

<!---
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header pull-left">
<a class="navbar-brand" href="#">Microservices Fabric BookInfo Demo</a>
</div>
<div class="navbar-header pull-right">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
</div>
<div class="navbar-collapse collapse">
<a href="logout"><button type="button" class="btn btn-default navbar-btn pull-right">Sign out</button></a>
<p class="navbar-text pull-right">Signed in as normal-user</p>
</div>
</div>
</div>
-->

<div id="login-modal" class="modal fade" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h4 class="modal-title">Please sign in</h4>
</div>
<div class="modal-body">
<form method="post" action='login' name="login_form">
<p><input type="text" class="form-control" name="username" id="username" placeholder="User Name"></p>
<p><input type="password" class="form-control" name="passwd" placeholder="Password"></p>
<p>
<button type="submit" class="btn btn-primary">Sign in</button>
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
</p>
</form>
</div>
</div>

</div>
</div>

<div class="container-fluid">
<div class="row">
<div class="col-md-12">
<h3 class="text-center text-primary">The Comedy of Errors</h3>

<p>Summary: <a href="https://en.wikipedia.org/wiki/The_Comedy_of_Errors">Wikipedia Summary</a>: The Comedy of Errors is one of <b>William Shakespeare's</b> early plays. It is his shortest and one of his most farcical comedies, with a major part of the humour coming from slapstick and mistaken identity, in addition to puns and word play.</p>

</div>
</div>

<div class="row">
<div class="col-md-6">

<h4 class="text-center text-primary">Book Details</h4>
<dl>
<dt>Type:</dt>paperback
<dt>Pages:</dt>200
<dt>Publisher:</dt>PublisherA
<dt>Language:</dt>English
<dt>ISBN-10:</dt>1234567890
<dt>ISBN-13:</dt>123-1234567890
</dl>

</div>

<div class="col-md-6">

<h4 class="text-center text-primary">Book Reviews</h4>

<blockquote>
<p>An extremely entertaining play by Shakespeare. The slapstick humour is refreshing!</p>
<small>Reviewer1</small>


<font color="black">
<!-- full stars: -->

<span class="glyphicon glyphicon-star"></span>

<span class="glyphicon glyphicon-star"></span>

<span class="glyphicon glyphicon-star"></span>

<span class="glyphicon glyphicon-star"></span>

<span class="glyphicon glyphicon-star"></span>

<!-- empty stars: -->

</font>


</blockquote>

<blockquote>
<p>Absolutely fun and entertaining. The play lacks thematic depth when compared to other plays by Shakespeare.</p>
<small>Reviewer2</small>


<font color="black">
<!-- full stars: -->

<span class="glyphicon glyphicon-star"></span>

<span class="glyphicon glyphicon-star"></span>

<span class="glyphicon glyphicon-star"></span>

<span class="glyphicon glyphicon-star"></span>

<!-- empty stars: -->

<span class="glyphicon glyphicon-star-empty"></span>

</font>


</blockquote>


</div>
</div>
</div>



<!-- Latest compiled and minified JavaScript -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>

<!-- Latest compiled and minified JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>

<script type="text/javascript">
$('#login-modal').on('shown.bs.modal', function () {
$('#username').focus();
});
</script>

</body>
</html>
74 changes: 55 additions & 19 deletions tests/e2e/tests/bookinfo/demo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ const (
yamlExtension = "yaml"
deploymentDir = "kube"
routeRulesDir = "kube"
tutorialDir = "istio.io_tutorial"
bookinfoYaml = "bookinfo"
bookinfoRatingsv2Yaml = "bookinfo-ratings-v2"
bookinfoRatingsMysqlYaml = "bookinfo-ratings-v2-mysql"
Expand All @@ -50,6 +51,8 @@ const (
modelDir = "tests/apps/bookinfo/output"
allRule = routeRulesDir + "/" + "route-rule-all-v1"
delayRule = routeRulesDir + "/" + "route-rule-ratings-test-delay"
tenRule = tutorialDir + "/" + "/route-rule-reviews-90-10"
twentyRule = tutorialDir + "/" + "route-rule-reviews-80-20"
fiftyRule = routeRulesDir + "/" + "route-rule-reviews-50-v3"
testRule = routeRulesDir + "/" + "route-rule-reviews-test-v2"
testDbRule = routeRulesDir + "/" + "route-rule-ratings-db"
Expand Down Expand Up @@ -93,8 +96,8 @@ func closeResponseBody(r *http.Response) {

func (t *testConfig) Setup() error {
//generate rule yaml files, replace "jason" with actual user
for _, rule := range []string{allRule, delayRule, fiftyRule, testRule, testDbRule, testMysqlRule,
detailsExternalServiceRouteRule, detailsExternalServiceEgressRule} {
for _, rule := range []string{allRule, delayRule, tenRule, twentyRule, fiftyRule, testRule,
testDbRule, testMysqlRule, detailsExternalServiceRouteRule, detailsExternalServiceEgressRule} {
src := util.GetResourcePath(filepath.Join(bookinfoSampleDir, rule+"."+yamlExtension))
dest := filepath.Join(t.rulesDir, rule+"."+yamlExtension)
ori, err := ioutil.ReadFile(src)
Expand Down Expand Up @@ -368,20 +371,47 @@ func TestFaultDelay(t *testing.T) {
}
}

type migrationRule struct {
key string
rate float64
modelToMigrate string
}

func TestVersionMigration(t *testing.T) {
var rules = []string{fiftyRule}
inspect(applyRules(rules), "failed to apply rules", "", t)
modelV2 := util.GetResourcePath(filepath.Join(modelDir, "productpage-normal-user-v2.html"))
modelV3 := util.GetResourcePath(filepath.Join(modelDir, "productpage-normal-user-v3.html"))

var rules = []migrationRule{
{
key: fiftyRule,
modelToMigrate: modelV3,
rate: 0.5,
},
{
key: twentyRule,
modelToMigrate: modelV2,
rate: 0.2,
},
{
key: tenRule,
modelToMigrate: modelV2,
rate: 0.1,
},
}

for _, rule := range rules {
doTestVersionMigration(t, rule)
}
}

func doTestVersionMigration(t *testing.T, rule migrationRule) {
inspect(applyRules([]string{rule.key}), "failed to apply rules", "", t)
defer func() {
inspect(deleteRules(rules), fmt.Sprintf("failed to delete rules"), "", t)
inspect(deleteRules([]string{rule.key}), fmt.Sprintf("failed to delete rules"), "", t)
}()

// Percentage moved to new version
migrationRate := 0.5
modelV1 := util.GetResourcePath(filepath.Join(modelDir, "productpage-normal-user-v1.html"))
tolerance := 0.05
totalShot := 100
modelV1 := util.GetResourcePath(filepath.Join(modelDir, "productpage-normal-user-v1.html"))
modelV3 := util.GetResourcePath(filepath.Join(modelDir, "productpage-normal-user-v3.html"))

cookies := []http.Cookie{
{
Name: "foo",
Expand All @@ -394,7 +424,7 @@ func TestVersionMigration(t *testing.T) {
}

for i := 0; i < testRetryTimes; i++ {
c1, c3 := 0, 0
c1, cVersionToMigrate := 0, 0
for c := 0; c < totalShot; c++ {
resp, err := getWithCookie(fmt.Sprintf("%s/productpage", tc.Kube.IngressOrFail(t)), cookies)
inspect(err, "Failed to record", "", t)
Expand All @@ -409,23 +439,23 @@ func TestVersionMigration(t *testing.T) {
}
if err = util.CompareToFile(body, modelV1); err == nil {
c1++
} else if err = util.CompareToFile(body, modelV3); err == nil {
c3++
} else if err = util.CompareToFile(body, rule.modelToMigrate); err == nil {
cVersionToMigrate++
}
closeResponseBody(resp)
}
c1Percent := int((migrationRate + tolerance) * float64(totalShot))
c3Percent := int((migrationRate - tolerance) * float64(totalShot))
if (c1 <= c1Percent) && (c3 >= c3Percent) {

if isWithinPercentage(c1, totalShot, 1.0-rule.rate, tolerance) &&
isWithinPercentage(cVersionToMigrate, totalShot, rule.rate, tolerance) {
log.Infof(
"Success! Version migration acts as expected, "+
"old version hit %d, new version hit %d", c1, c3)
"old version hit %d, new version hit %d", c1, cVersionToMigrate)
break
}

if i == testRetryTimes-1 {
t.Errorf("Failed version migration test, "+
"old version hit %d, new version hit %d", c1, c3)
"old version hit %d, new version hit %d", c1, cVersionToMigrate)
}
}
}
Expand All @@ -435,6 +465,12 @@ func getBookinfoResourcePath(resource string) string {
resource+"."+yamlExtension))
}

func isWithinPercentage(count int, total int, rate float64, tolerance float64) bool {
minimum := int((rate - tolerance) * float64(total))
maximum := int((rate + tolerance) * float64(total))
return count >= minimum && count <= maximum
}

func setTestConfig() error {
cc, err := framework.NewCommonConfig("demo_test")
if err != nil {
Expand Down

0 comments on commit 6403d79

Please sign in to comment.