/
service.go
146 lines (121 loc) · 4.41 KB
/
service.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
// Copyright (c) Bas van Beek 2022.
// Copyright (c) Tetrate, Inc 2021.
//
// 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 service
import (
"errors"
"fmt"
"net/http"
"sync"
"time"
"github.com/gorilla/mux"
zmw "github.com/openzipkin/zipkin-go/middleware/http"
"github.com/tetratelabs/multierror"
"github.com/tetratelabs/run"
"github.com/basvanbeek/topology-tester/pkg"
"github.com/basvanbeek/topology-tester/pkg/zipkin"
)
const (
flagDuration = "ep-duration"
flagErrors = "ep-errors"
flagHeaders = "ep-headers"
flagHandleFailures = "ep-handle-failures"
errProxyService pkg.Error = "invalid or no proxy service set"
errPercentage pkg.Error = "expected percentage value between 0 and 100"
errDuration pkg.Error = "expected a zero or positive duration"
errConcurrency pkg.Error = "invalid or no concurrency type set"
errInternal pkg.Error = "internal service failure occurred"
errHandleFailures pkg.Error = "expected boolean value for handling failures"
)
// Endpoints implements a run.Config compatible group of Endpoints which will
// register themselves on the provided http service, using the provided Zipkin
// tracer to instrument themselves.
type Endpoints struct {
// dependencies
SvcTracer *zipkin.Service
ServiceName string
handler http.Handler
tracer *zipkin.Tracer
// service globals protected by mutex mtx
mtx sync.RWMutex
errors int32
headers int32
duration time.Duration
handleFailures bool
}
// Name implements run.Unit.
func (ep *Endpoints) Name() string {
return "endpoints"
}
// FlagSet implements run.Config.
func (ep *Endpoints) FlagSet() *run.FlagSet {
flags := run.NewFlagSet("Endpoint options")
flags.Int32Var(&ep.errors, flagErrors, ep.errors,
`Percentage of errors on echo handler`)
flags.Int32Var(&ep.headers, flagHeaders, ep.headers,
`Percentage of double headers on echo handler`)
flags.DurationVar(&ep.duration, flagDuration, ep.duration,
`Duration of a request on echo handler`)
flags.BoolVar(&ep.handleFailures, flagHandleFailures, ep.handleFailures,
`Handle failures when proxying and return OK to requestor`)
return flags
}
// Validate implements run.Config.
func (ep *Endpoints) Validate() error {
var mErr error
if ep.errors < 0 || ep.errors > 100 {
mErr = multierror.Append(mErr,
fmt.Errorf(pkg.FlagErr, flagErrors, errPercentage),
)
}
if ep.headers < 0 || ep.headers > 100 {
mErr = multierror.Append(mErr,
fmt.Errorf(pkg.FlagErr, flagHeaders, errPercentage),
)
}
if ep.duration < 0 {
mErr = multierror.Append(mErr,
fmt.Errorf(pkg.FlagErr, flagDuration, errDuration),
)
}
return mErr
}
// PreRun implements run.PreRunner.
func (ep *Endpoints) PreRun() error {
if ep.SvcTracer == nil || ep.SvcTracer.GetTracer() == nil {
return errors.New("missing Zipkin tracer to attach to")
}
// create our service router
router := mux.NewRouter()
router.Methods("GET").Path("/headers/{percentage}").HandlerFunc(ep.setDoubleHeaders)
router.Methods("GET").Path("/errors/{percentage}").HandlerFunc(ep.setErrors)
router.Methods("GET").Path("/graceful/{handleFailures}").HandlerFunc(ep.setHandleFailures)
router.Methods("GET").Path("/latency/{duration}").HandlerFunc(ep.setLatency)
router.Methods("GET").Path("/crash/{message}").HandlerFunc(ep.crash)
router.Methods("GET").Path("/local/{concurrency}/latency/{duration}").HandlerFunc(ep.emulateConcurrency)
router.Methods("GET").PathPrefix("/proxy/{service}").HandlerFunc(ep.proxy)
router.Methods("GET").PathPrefix("/").HandlerFunc(ep.echoHandler)
ep.tracer = ep.SvcTracer.GetTracer()
ep.handler = zmw.NewServerMiddleware(ep.tracer)(router)
return nil
}
// Handler returns an HTTP handler that can be attached to an HTTP service.
// The handler holds a router to the endpoints with the sub handlers.
func (ep *Endpoints) Handler() http.Handler {
return ep.handler
}
var (
_ run.Config = (*Endpoints)(nil)
_ run.PreRunner = (*Endpoints)(nil)
)