Skip to content

Commit

Permalink
support traffic control (#308)
Browse files Browse the repository at this point in the history
* support traffic control

* fix log when no instance is UP

* Update pkg/object/meshcontroller/worker/egress.go

Co-authored-by: Yun Long <[email protected]>

Co-authored-by: Yun Long <[email protected]>
  • Loading branch information
localvar and xxx7xxxx committed Oct 23, 2021
1 parent 101c34d commit 465736c
Show file tree
Hide file tree
Showing 13 changed files with 945 additions and 118 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ require (
github.com/json-iterator/go v1.1.11
github.com/klauspost/compress v1.13.1
github.com/lucas-clemente/quic-go v0.21.1
github.com/megaease/easemesh-api v1.3.2
github.com/megaease/easemesh-api v1.3.3
github.com/megaease/grace v1.0.0
github.com/mitchellh/mapstructure v1.4.1
github.com/nacos-group/nacos-sdk-go v1.0.8
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -869,8 +869,8 @@ github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpe
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/megaease/easemesh-api v1.3.2 h1:0yXyG0QcI0L6biCQKED5NCT2DTmnDQfAq2q9hIktTcE=
github.com/megaease/easemesh-api v1.3.2/go.mod h1:cNKMXb0iK3M+woe0JzivXFfnvP0E1MeF+zbeWFEuU7g=
github.com/megaease/easemesh-api v1.3.3 h1:jaTdi6KkZ1BoQuqBDtV22HEI5GXNjsj+rhum6+vUnYs=
github.com/megaease/easemesh-api v1.3.3/go.mod h1:cNKMXb0iK3M+woe0JzivXFfnvP0E1MeF+zbeWFEuU7g=
github.com/megaease/grace v1.0.0 h1:b44R3j6e/iaN62F4ZUnru9nzL1VaIcxxUZjSPVtTVzI=
github.com/megaease/grace v1.0.0/go.mod h1:mOR6MVYQ6zGyuz9Y2or/VJ6QWueTL3erxWfIwyCmiIg=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
Expand Down
5 changes: 2 additions & 3 deletions pkg/object/function/worker/ingress.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,9 +248,8 @@ func (ings *ingressServer) add(pipeline string) error {
PathPrefix: "/",
Headers: []*httpserver.Header{
{
Key: ingressFunctionKey,
Values: []string{pipeline},
Backend: pipeline,
Key: ingressFunctionKey,
Values: []string{pipeline},
},
},
Backend: pipeline,
Expand Down
7 changes: 3 additions & 4 deletions pkg/object/httpserver/spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,9 @@ type (
// the headers entry will only be checked after a path entry matched. However, the headers entry has a higher priority
// than the path entry itself.
Header struct {
Key string `yaml:"key" jsonschema:"required"`
Values []string `yaml:"values,omitempty" jsonschema:"omitempty,uniqueItems=true"`
Regexp string `yaml:"regexp,omitempty" jsonschema:"omitempty,format=regexp"`
Backend string `yaml:"backend" jsonschema:"required"`
Key string `yaml:"key" jsonschema:"required"`
Values []string `yaml:"values,omitempty" jsonschema:"omitempty,uniqueItems=true"`
Regexp string `yaml:"regexp,omitempty" jsonschema:"omitempty,format=regexp"`

headerRE *regexp.Regexp
}
Expand Down
24 changes: 24 additions & 0 deletions pkg/object/meshcontroller/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,18 @@ const (
// MeshServiceInstancePath is the mesh service path.
MeshServiceInstancePath = "/mesh/serviceinstances/{serviceName}/{instanceID}"

// MeshHTTPRouteGroupPrefix is the mesh HTTP route groups prefix.
MeshHTTPRouteGroupPrefix = "/mesh/httproutegroups"

// MeshHTTPRouteGroupPath is the mesh HTTP route groups path.
MeshHTTPRouteGroupPath = "/mesh/httproutegroups/{name}"

// MeshTrafficTargetPrefix is the mesh traffic target prefix.
MeshTrafficTargetPrefix = "/mesh/traffictargets"

// MeshTrafficTargetPath is the mesh traffic target path.
MeshTrafficTargetPath = "/mesh/traffictargets/{name}"

// MeshCustomResourceKindPrefix is the mesh custom resource kind prefix.
MeshCustomResourceKindPrefix = "/mesh/customresourcekinds"

Expand Down Expand Up @@ -183,6 +195,18 @@ func (a *API) registerAPIs() {
{Path: MeshServiceMetricsPath, Method: "PUT", Handler: a.updatePartOfService(metricsMeta)},
{Path: MeshServiceMetricsPath, Method: "DELETE", Handler: a.deletePartOfService(metricsMeta)},

{Path: MeshHTTPRouteGroupPrefix, Method: "GET", Handler: a.listHTTPRouteGroups},
{Path: MeshHTTPRouteGroupPrefix, Method: "POST", Handler: a.createHTTPRouteGroup},
{Path: MeshHTTPRouteGroupPath, Method: "GET", Handler: a.getHTTPRouteGroup},
{Path: MeshHTTPRouteGroupPath, Method: "PUT", Handler: a.updateHTTPRouteGroup},
{Path: MeshHTTPRouteGroupPath, Method: "DELETE", Handler: a.deleteHTTPRouteGroup},

{Path: MeshTrafficTargetPrefix, Method: "GET", Handler: a.listTrafficTargets},
{Path: MeshTrafficTargetPrefix, Method: "POST", Handler: a.createTrafficTarget},
{Path: MeshTrafficTargetPath, Method: "GET", Handler: a.getTrafficTarget},
{Path: MeshTrafficTargetPath, Method: "PUT", Handler: a.updateTrafficTarget},
{Path: MeshTrafficTargetPath, Method: "DELETE", Handler: a.deleteTrafficTarget},

{Path: MeshCustomResourceKindPrefix, Method: "GET", Handler: a.listCustomResourceKinds},
{Path: MeshCustomResourceKindPrefix, Method: "POST", Handler: a.createCustomResourceKind},
{Path: MeshCustomResourceKind, Method: "GET", Handler: a.getCustomResourceKind},
Expand Down
257 changes: 257 additions & 0 deletions pkg/object/meshcontroller/api/api_trafficcontrol.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
/*
* Copyright (c) 2017, MegaEase
* All rights reserved.
*
* 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:https://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 api

import (
"encoding/json"
"fmt"
"net/http"
"sort"

"github.com/megaease/easegress/pkg/api"
"github.com/megaease/easegress/pkg/logger"
"github.com/megaease/easegress/pkg/object/meshcontroller/spec"
"github.com/megaease/easemesh-api/v1alpha1"
)

func (a *API) listHTTPRouteGroups(w http.ResponseWriter, r *http.Request) {
groups := a.service.ListHTTPRouteGroups()
sort.Slice(groups, func(i, j int) bool {
return groups[i].Name < groups[j].Name
})

var pbGroups []*v1alpha1.HTTPRouteGroup
for _, v := range groups {
group := &v1alpha1.HTTPRouteGroup{}
err := a.convertSpecToPB(v, group)
if err != nil {
logger.Errorf("convert spec %#v to pb spec failed: %v", v, err)
continue
}
pbGroups = append(pbGroups, group)
}

err := json.NewEncoder(w).Encode(pbGroups)
if err != nil {
panic(fmt.Errorf("marshal %#v to json failed: %v", groups, err))
}

w.Header().Set("Content-Type", "application/json")
}

func (a *API) getHTTPRouteGroup(w http.ResponseWriter, r *http.Request) {
name, err := a.readURLParam(r, "name")
if err != nil {
api.HandleAPIError(w, r, http.StatusBadRequest, err)
return
}

group := a.service.GetHTTPRouteGroup(name)
if group == nil {
api.HandleAPIError(w, r, http.StatusNotFound, fmt.Errorf("%s not found", name))
return
}

pbGroup := &v1alpha1.HTTPRouteGroup{}
err = a.convertSpecToPB(group, pbGroup)
if err != nil {
panic(fmt.Errorf("convert spec %#v to pb failed: %v", group, err))
}

err = json.NewEncoder(w).Encode(pbGroup)
if err != nil {
panic(fmt.Errorf("marshal %#v to json failed: %v", pbGroup, err))
}

w.Header().Set("Content-Type", "application/json")
}

func (a *API) saveHTTPRouteGroup(w http.ResponseWriter, r *http.Request, update bool) error {
pbGroup := &v1alpha1.HTTPRouteGroup{}
group := &spec.HTTPRouteGroup{}

err := a.readAPISpec(r, pbGroup, group)
if err != nil {
api.HandleAPIError(w, r, http.StatusBadRequest, err)
return err
}

name := group.Name

a.service.Lock()
defer a.service.Unlock()

oldGroup := a.service.GetHTTPRouteGroup(name)
if update && (oldGroup == nil) {
err = fmt.Errorf("%s not found", name)
api.HandleAPIError(w, r, http.StatusNotFound, err)
return err
}
if (!update) && (oldGroup != nil) {
err = fmt.Errorf("%s existed", name)
api.HandleAPIError(w, r, http.StatusConflict, err)
return err
}

a.service.PutHTTPRouteGroup(group)
return nil
}

func (a *API) createHTTPRouteGroup(w http.ResponseWriter, r *http.Request) {
err := a.saveHTTPRouteGroup(w, r, false)
if err == nil {
w.Header().Set("Location", r.URL.Path)
w.WriteHeader(http.StatusCreated)
}
}

func (a *API) updateHTTPRouteGroup(w http.ResponseWriter, r *http.Request) {
a.saveHTTPRouteGroup(w, r, true)
}

func (a *API) deleteHTTPRouteGroup(w http.ResponseWriter, r *http.Request) {
name, err := a.readURLParam(r, "name")
if err != nil {
api.HandleAPIError(w, r, http.StatusBadRequest, err)
return
}

a.service.Lock()
defer a.service.Unlock()

oldGroup := a.service.GetHTTPRouteGroup(name)
if oldGroup == nil {
api.HandleAPIError(w, r, http.StatusNotFound, fmt.Errorf("%s not found", name))
return
}

a.service.DeleteHTTPRouteGroup(name)
}

func (a *API) listTrafficTargets(w http.ResponseWriter, r *http.Request) {
tts := a.service.ListTrafficTargets()
sort.Slice(tts, func(i, j int) bool {
return tts[i].Name < tts[j].Name
})

var pbTrafficTargets []*v1alpha1.TrafficTarget
for _, v := range tts {
tt := &v1alpha1.TrafficTarget{}
err := a.convertSpecToPB(v, tt)
if err != nil {
logger.Errorf("convert spec %#v to pb spec failed: %v", v, err)
continue
}
pbTrafficTargets = append(pbTrafficTargets, tt)
}

err := json.NewEncoder(w).Encode(pbTrafficTargets)
if err != nil {
panic(fmt.Errorf("marshal %#v to json failed: %v", tts, err))
}

w.Header().Set("Content-Type", "application/json")
}

func (a *API) getTrafficTarget(w http.ResponseWriter, r *http.Request) {
name, err := a.readURLParam(r, "name")
if err != nil {
api.HandleAPIError(w, r, http.StatusBadRequest, err)
return
}

tt := a.service.GetTrafficTarget(name)
if tt == nil {
api.HandleAPIError(w, r, http.StatusNotFound, fmt.Errorf("%s not found", name))
return
}

pbTrafficTarget := &v1alpha1.TrafficTarget{}
err = a.convertSpecToPB(tt, pbTrafficTarget)
if err != nil {
panic(fmt.Errorf("convert spec %#v to pb failed: %v", tt, err))
}

err = json.NewEncoder(w).Encode(pbTrafficTarget)
if err != nil {
panic(fmt.Errorf("marshal %#v to json failed: %v", pbTrafficTarget, err))
}

w.Header().Set("Content-Type", "application/json")
}

func (a *API) saveTrafficTarget(w http.ResponseWriter, r *http.Request, update bool) error {
pbTrafficTarget := &v1alpha1.TrafficTarget{}
tt := &spec.TrafficTarget{}

err := a.readAPISpec(r, pbTrafficTarget, tt)
if err != nil {
api.HandleAPIError(w, r, http.StatusBadRequest, err)
return err
}
name := tt.Name

a.service.Lock()
defer a.service.Unlock()

oldTrafficTarget := a.service.GetTrafficTarget(name)
if update && (oldTrafficTarget == nil) {
err = fmt.Errorf("%s not found", name)
api.HandleAPIError(w, r, http.StatusNotFound, err)
return err
}
if (!update) && (oldTrafficTarget != nil) {
err = fmt.Errorf("%s existed", name)
api.HandleAPIError(w, r, http.StatusConflict, err)
return err
}

a.service.PutTrafficTarget(tt)
return nil
}

func (a *API) createTrafficTarget(w http.ResponseWriter, r *http.Request) {
err := a.saveTrafficTarget(w, r, false)
if err == nil {
w.Header().Set("Location", r.URL.Path)
w.WriteHeader(http.StatusCreated)
}
}

func (a *API) updateTrafficTarget(w http.ResponseWriter, r *http.Request) {
a.saveTrafficTarget(w, r, true)
}

func (a *API) deleteTrafficTarget(w http.ResponseWriter, r *http.Request) {
name, err := a.readURLParam(r, "name")
if err != nil {
api.HandleAPIError(w, r, http.StatusBadRequest, err)
return
}

a.service.Lock()
defer a.service.Unlock()

oldTrafficTarget := a.service.GetTrafficTarget(name)
if oldTrafficTarget == nil {
api.HandleAPIError(w, r, http.StatusNotFound, fmt.Errorf("%s not found", name))
return
}

a.service.DeleteTrafficTarget(name)
}
Loading

0 comments on commit 465736c

Please sign in to comment.