Merge pull request #1124 from pescox/pr/path-rewrite

path-rewrite feature
This commit is contained in:
yangkaa 2022-01-27 00:21:59 -06:00 committed by GitHub
commit 1df4e8c23f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 280 additions and 17 deletions

View File

@ -94,11 +94,27 @@ func (g *GatewayAction) CreateHTTPRule(tx *gorm.DB, req *apimodel.AddHTTPRuleStr
Weight: req.Weight,
IP: req.IP,
CertificateID: req.CertificateID,
PathRewrite: req.PathRewrite,
}
if err := db.GetManager().HTTPRuleDaoTransactions(tx).AddModel(httpRule); err != nil {
return fmt.Errorf("create http rule: %v", err)
}
if len(req.Rewrites) > 0 {
for _, rewrite := range req.Rewrites {
r := &model.HTTPRuleRewrite{
UUID: util.NewUUID(),
HTTPRuleID: httpRule.UUID,
Regex: rewrite.Regex,
Replacement: rewrite.Replacement,
Flag: rewrite.Flag,
}
if err := db.GetManager().HTTPRuleRewriteDaoTransactions(tx).AddModel(r); err != nil {
return fmt.Errorf("create http rule rewrite: %v", err)
}
}
}
if strings.Replace(req.CertificateID, " ", "", -1) != "" {
cert := &model.Certificate{
UUID: req.CertificateID,
@ -144,6 +160,29 @@ func (g *GatewayAction) UpdateHTTPRule(req *apimodel.UpdateHTTPRuleStruct) error
tx.Rollback()
return fmt.Errorf("HTTPRule dosen't exist based on uuid(%s)", req.HTTPRuleID)
}
// delete old http rule rewrites
if err := db.GetManager().HTTPRuleRewriteDaoTransactions(tx).DeleteByHTTPRuleID(rule.UUID); err != nil {
tx.Rollback()
return err
}
if len(req.Rewrites) > 0 {
// add new http rule rewrites
for _, rewrite := range req.Rewrites {
r := &model.HTTPRuleRewrite{
UUID: util.NewUUID(),
HTTPRuleID: rule.UUID,
Regex: rewrite.Regex,
Replacement: rewrite.Replacement,
Flag: rewrite.Flag,
}
if err := db.GetManager().HTTPRuleRewriteDaoTransactions(tx).AddModel(r); err != nil {
tx.Rollback()
return err
}
}
}
if strings.Replace(req.CertificateID, " ", "", -1) != "" {
// add new certificate
cert := &model.Certificate{
@ -198,6 +237,7 @@ func (g *GatewayAction) UpdateHTTPRule(req *apimodel.UpdateHTTPRuleStruct) error
rule.Header = req.Header
rule.Cookie = req.Cookie
rule.Weight = req.Weight
rule.PathRewrite = req.PathRewrite
if req.IP != "" {
rule.IP = req.IP
}
@ -243,6 +283,12 @@ func (g *GatewayAction) DeleteHTTPRule(req *apimodel.DeleteHTTPRuleStruct) error
return err
}
// delete http rule rewrites
if err := db.GetManager().HTTPRuleRewriteDaoTransactions(tx).DeleteByHTTPRuleID(httpRule.UUID); err != nil {
tx.Rollback()
return err
}
// delete rule extension
if err := g.dbmanager.RuleExtensionDaoTransactions(tx).DeleteRuleExtensionByRuleID(httpRule.UUID); err != nil {
tx.Rollback()
@ -272,6 +318,9 @@ func (g *GatewayAction) DeleteHTTPRuleByServiceIDWithTransaction(sid string, tx
}
for _, rule := range rules {
if err := db.GetManager().HTTPRuleRewriteDaoTransactions(tx).DeleteByHTTPRuleID(rule.UUID); err != nil {
return err
}
if err := g.dbmanager.RuleExtensionDaoTransactions(tx).DeleteRuleExtensionByRuleID(rule.UUID); err != nil {
return err
}
@ -885,9 +934,10 @@ func (g *GatewayAction) listHTTPRuleIDs(componentID string, port int) ([]string,
// SyncHTTPRules -
func (g *GatewayAction) SyncHTTPRules(tx *gorm.DB, components []*apimodel.Component) error {
var (
componentIDs []string
httpRules []*model.HTTPRule
ruleExtensions []*model.RuleExtension
componentIDs []string
httpRules []*model.HTTPRule
ruleExtensions []*model.RuleExtension
httpRuleRewrites []*model.HTTPRuleRewrite
)
for _, component := range components {
if component.HTTPRules == nil {
@ -897,6 +947,16 @@ func (g *GatewayAction) SyncHTTPRules(tx *gorm.DB, components []*apimodel.Compon
for _, httpRule := range component.HTTPRules {
httpRules = append(httpRules, httpRule.DbModel(component.ComponentBase.ComponentID))
for _, rewrite := range httpRule.Rewrites {
httpRuleRewrites = append(httpRuleRewrites, &model.HTTPRuleRewrite{
UUID: util.NewUUID(),
HTTPRuleID: httpRule.HTTPRuleID,
Regex: rewrite.Regex,
Replacement: rewrite.Replacement,
Flag: rewrite.Flag,
})
}
for _, ext := range httpRule.RuleExtensions {
ruleExtensions = append(ruleExtensions, &model.RuleExtension{
UUID: util.NewUUID(),
@ -908,6 +968,10 @@ func (g *GatewayAction) SyncHTTPRules(tx *gorm.DB, components []*apimodel.Compon
}
}
if err := g.syncHTTPRuleRewrites(tx, httpRules, httpRuleRewrites); err != nil {
return err
}
if err := g.syncRuleExtensions(tx, httpRules, ruleExtensions); err != nil {
return err
}
@ -918,6 +982,18 @@ func (g *GatewayAction) SyncHTTPRules(tx *gorm.DB, components []*apimodel.Compon
return db.GetManager().HTTPRuleDaoTransactions(tx).CreateOrUpdateHTTPRuleInBatch(httpRules)
}
func (g *GatewayAction) syncHTTPRuleRewrites(tx *gorm.DB, httpRules []*model.HTTPRule, rewrites []*model.HTTPRuleRewrite) error {
var ruleIDs []string
for _, hr := range httpRules {
ruleIDs = append(ruleIDs, hr.UUID)
}
if err := db.GetManager().HTTPRuleRewriteDaoTransactions(tx).DeleteByHTTPRuleIDs(ruleIDs); err != nil {
return err
}
return db.GetManager().HTTPRuleRewriteDaoTransactions(tx).CreateOrUpdateHTTPRuleRewriteInBatch(rewrites)
}
func (g *GatewayAction) syncRuleExtensions(tx *gorm.DB, httpRules []*model.HTTPRule, exts []*model.RuleExtension) error {
var ruleIDs []string
for _, hr := range httpRules {

View File

@ -40,6 +40,8 @@ type AddHTTPRuleStruct struct {
Certificate string `json:"certificate"`
PrivateKey string `json:"private_key"`
RuleExtensions []*RuleExtensionStruct `json:"rule_extensions"`
PathRewrite bool `json:"path_rewrite"`
Rewrites []*Rewrite `json:"rewrites"`
}
// DbModel return database model
@ -60,6 +62,7 @@ func (h *AddHTTPRuleStruct) DbModel(serviceID string) *dbmodel.HTTPRule {
Weight: h.Weight,
IP: h.IP,
CertificateID: h.CertificateID,
PathRewrite: h.PathRewrite,
}
}
@ -78,6 +81,8 @@ type UpdateHTTPRuleStruct struct {
Certificate string `json:"certificate"`
PrivateKey string `json:"private_key"`
RuleExtensions []*RuleExtensionStruct `json:"rule_extensions"`
PathRewrite bool `json:"path_rewrite"`
Rewrites []*Rewrite `json:"rewrites"`
}
//DeleteHTTPRuleStruct contains the id of http rule that will be deleted
@ -254,9 +259,9 @@ type SetHeader struct {
// Rewrite is a embeded sturct of Body.
type Rewrite struct {
Regex string `json:"regex"`
Replacement string `json:"replacement"`
Flag string `json:"flag" validate:"flag|in:last,break,redirect,permanent"`
Regex string `json:"regex" validate:"regex|required"`
Replacement string `json:"replacement" validate:"replacement|required"`
Flag string `json:"flag" validate:"flag|in:last,break,redirect,permanent|required"`
}
// UpdCertificateReq -

View File

@ -527,6 +527,15 @@ type HTTPRuleDao interface {
ListByComponentIDs(componentIDs []string) ([]*model.HTTPRule, error)
}
// HTTPRuleRewriteDao -
type HTTPRuleRewriteDao interface {
Dao
CreateOrUpdateHTTPRuleRewriteInBatch(httpRuleRewrites []*model.HTTPRuleRewrite) error
ListByHTTPRuleID(httpRuleID string) ([]*model.HTTPRuleRewrite, error)
DeleteByHTTPRuleID(httpRuleID string) error
DeleteByHTTPRuleIDs(httpRuleIDs []string) error
}
// TCPRuleDao -
type TCPRuleDao interface {
Dao

View File

@ -115,6 +115,8 @@ type Manager interface {
RuleExtensionDaoTransactions(db *gorm.DB) dao.RuleExtensionDao
HTTPRuleDao() dao.HTTPRuleDao
HTTPRuleDaoTransactions(db *gorm.DB) dao.HTTPRuleDao
HTTPRuleRewriteDao() dao.HTTPRuleRewriteDao
HTTPRuleRewriteDaoTransactions(db *gorm.DB) dao.HTTPRuleRewriteDao
TCPRuleDao() dao.TCPRuleDao
TCPRuleDaoTransactions(db *gorm.DB) dao.TCPRuleDao
GwRuleConfigDao() dao.GwRuleConfigDao

View File

@ -82,6 +82,7 @@ type HTTPRule struct {
Weight int `gorm:"column:weight"`
IP string `gorm:"column:ip"`
CertificateID string `gorm:"column:certificate_id"`
PathRewrite bool `gorm:"column:path_rewrite"`
}
// TableName returns table name of TCPRule
@ -89,6 +90,21 @@ func (TCPRule) TableName() string {
return "gateway_tcp_rule"
}
// HTTPRuleRewrite containe http rule rewrites
type HTTPRuleRewrite struct {
Model
UUID string `gorm:"column:uuid"`
HTTPRuleID string `gorm:"column:http_rule_id"`
Regex string `gorm:"column:regex"`
Replacement string `gorm:"column:replacement"`
Flag string `gorm:"column:flag"`
}
// TableName retuens table name of HTTPRuleRewrite
func (HTTPRuleRewrite) TableName() string {
return "gateway_http_rule_rewrite"
}
// TCPRule contain stream rule
type TCPRule struct {
Model

View File

@ -313,6 +313,70 @@ func (h *HTTPRuleDaoImpl) ListByComponentIDs(componentIDs []string) ([]*model.HT
return rules, nil
}
// HTTPRuleRewriteDaoTmpl is a implementation of HTTPRuleRewriteDao
type HTTPRuleRewriteDaoTmpl struct {
DB *gorm.DB
}
//AddModel -
func (h *HTTPRuleRewriteDaoTmpl) AddModel(mo model.Interface) error {
httpRuleRewrite, ok := mo.(*model.HTTPRuleRewrite)
if !ok {
return fmt.Errorf("can't not convert %s to *model.HTTPRuleRewrite", reflect.TypeOf(mo).String())
}
var oldHTTPRuleRewrite model.HTTPRuleRewrite
if ok := h.DB.Where("uuid = ?", httpRuleRewrite.UUID).Find(&oldHTTPRuleRewrite).RecordNotFound(); !ok {
return fmt.Errorf("HTTPRuleRewrite already exists based on uuid(%s)", httpRuleRewrite.UUID)
}
return h.DB.Create(httpRuleRewrite).Error
}
//UpdateModel -
func (h *HTTPRuleRewriteDaoTmpl) UpdateModel(mo model.Interface) error {
hr, ok := mo.(*model.HTTPRuleRewrite)
if !ok {
return fmt.Errorf("failed to convert %s to *model.HTTPRuleRewrite", reflect.TypeOf(mo).String())
}
return h.DB.Save(hr).Error
}
// CreateOrUpdateHTTPRuleRewriteInBatch -
func (h *HTTPRuleRewriteDaoTmpl) CreateOrUpdateHTTPRuleRewriteInBatch(httpRuleRewrites []*model.HTTPRuleRewrite) error {
var objects []interface{}
for _, httpRuleRewrites := range httpRuleRewrites {
objects = append(objects, *httpRuleRewrites)
}
if err := gormbulkups.BulkUpsert(h.DB, objects, 2000); err != nil {
return errors.Wrap(err, "create or update http rule rewrite in batch")
}
return nil
}
// ListByHTTPRuleID -
func (h *HTTPRuleRewriteDaoTmpl) ListByHTTPRuleID(httpRuleID string) ([]*model.HTTPRuleRewrite, error) {
var rewrites []*model.HTTPRuleRewrite
if err := h.DB.Where("http_rule_id = ?", httpRuleID).Find(&rewrites).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return rewrites, nil
}
return nil, err
}
return rewrites, nil
}
// DeleteByHTTPRuleID -
func (h *HTTPRuleRewriteDaoTmpl) DeleteByHTTPRuleID(httpRuleID string) error {
return h.DB.Where("http_rule_id in (?) ", httpRuleID).Delete(&model.HTTPRuleRewrite{}).Error
}
// DeleteByHTTPRuleIDs deletes http rule rewrites by given httpRuleIDs.
func (h *HTTPRuleRewriteDaoTmpl) DeleteByHTTPRuleIDs(httpRuleIDs []string) error {
if err := h.DB.Where("http_rule_id in (?)", httpRuleIDs).Delete(&model.HTTPRuleRewrite{}).Error; err != nil {
return errors.Wrap(err, "delete http rule rewrites")
}
return nil
}
// TCPRuleDaoTmpl is a implementation of TcpRuleDao
type TCPRuleDaoTmpl struct {
DB *gorm.DB

View File

@ -522,6 +522,20 @@ func (m *Manager) HTTPRuleDaoTransactions(db *gorm.DB) dao.HTTPRuleDao {
}
}
// HTTPRuleRewriteDao HTTPRuleRewriteDao
func (m *Manager) HTTPRuleRewriteDao() dao.HTTPRuleRewriteDao {
return &mysqldao.HTTPRuleRewriteDaoTmpl{
DB: m.db,
}
}
//HTTPRuleRewriteDaoTransactions -
func (m *Manager) HTTPRuleRewriteDaoTransactions(db *gorm.DB) dao.HTTPRuleRewriteDao {
return &mysqldao.HTTPRuleRewriteDaoTmpl{
DB: db,
}
}
//TCPRuleDao TCPRuleDao
func (m *Manager) TCPRuleDao() dao.TCPRuleDao {
return &mysqldao.TCPRuleDaoTmpl{

View File

@ -141,6 +141,7 @@ func (m *Manager) RegisterTableModel() {
m.models = append(m.models, &model.Certificate{})
m.models = append(m.models, &model.RuleExtension{})
m.models = append(m.models, &model.HTTPRule{})
m.models = append(m.models, &model.HTTPRuleRewrite{})
m.models = append(m.models, &model.TCPRule{})
m.models = append(m.models, &model.TenantServiceConfigFile{})
m.models = append(m.models, &model.Endpoint{})

View File

@ -17,9 +17,10 @@ limitations under the License.
package rewrite
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"strings"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/goodrain/rainbond/gateway/annotations/parser"
"github.com/goodrain/rainbond/gateway/annotations/resolver"
"github.com/sirupsen/logrus"
@ -70,6 +71,21 @@ func (r1 *Config) Equal(r2 *Config) bool {
if r1.UseRegex != r2.UseRegex {
return false
}
if len(r1.Rewrites) != len(r2.Rewrites) {
return false
}
for _, r1r := range r1.Rewrites {
flag := false
for _, r2r := range r2.Rewrites {
if r1r.Regex == r2r.Regex && r1r.Replacement == r2r.Replacement && r1r.Flag == r2r.Flag {
flag = true
break
}
}
if !flag {
return false
}
}
return true
}

View File

@ -226,14 +226,14 @@ func (o *OrService) getNgxServer(conf *v1.Config) (l7srv []*model.Server, l4srv
location := &model.Location{
DisableAccessLog: o.ocfg.AccessLogPath == "",
// TODO: Distinguish between server output logs
AccessLogPath: o.ocfg.AccessLogPath,
EnableMetrics: true,
Path: loc.Path,
NameCondition: loc.NameCondition,
Proxy: loc.Proxy,
Rewrite: loc.Rewrite,
PathRewrite: false,
DisableProxyPass: loc.DisableProxyPass,
AccessLogPath: o.ocfg.AccessLogPath,
EnableMetrics: true,
Path: loc.Path,
NameCondition: loc.NameCondition,
Proxy: loc.Proxy,
Rewrite: loc.Rewrite,
PathRewrite: loc.PathRewrite,
DisableProxyPass: loc.DisableProxyPass,
}
server.Locations = append(server.Locations, location)
}
@ -253,6 +253,21 @@ func (o *OrService) getNgxServer(conf *v1.Config) (l7srv []*model.Server, l4srv
ProxyStreamNextUpstreamTries: 3,
}
server.Listen = strings.Join(vs.Listening, " ")
for _, loc := range vs.Locations {
location := &model.Location{
DisableAccessLog: o.ocfg.AccessLogPath == "",
// TODO: Distinguish between server output logs
AccessLogPath: o.ocfg.AccessLogPath,
EnableMetrics: true,
Path: loc.Path,
NameCondition: loc.NameCondition,
Proxy: loc.Proxy,
Rewrite: loc.Rewrite,
PathRewrite: loc.PathRewrite,
DisableProxyPass: loc.DisableProxyPass,
}
server.Locations = append(server.Locations, location)
}
l4srv = append(l4srv, server)
}
@ -271,7 +286,7 @@ func (o *OrService) UpdatePools(hpools []*v1.Pool, tpools []*v1.Pool) error {
logrus.Warningf("error updating upstream.default.tcp.conf")
}
}
if hpools == nil || len(hpools) == 0 {
if len(hpools) == 0 {
return nil
}
var backends []*model.Backend

View File

@ -23,7 +23,6 @@ import (
"crypto/x509"
"encoding/pem"
"fmt"
k8sutil "github.com/goodrain/rainbond/util/k8s"
"io/ioutil"
"net"
"os"
@ -32,10 +31,13 @@ import (
"strings"
"sync"
k8sutil "github.com/goodrain/rainbond/util/k8s"
"github.com/eapache/channels"
"github.com/goodrain/rainbond/cmd/gateway/option"
"github.com/goodrain/rainbond/gateway/annotations"
"github.com/goodrain/rainbond/gateway/annotations/l4"
"github.com/goodrain/rainbond/gateway/annotations/parser"
"github.com/goodrain/rainbond/gateway/annotations/rewrite"
"github.com/goodrain/rainbond/gateway/cluster"
"github.com/goodrain/rainbond/gateway/controller/config"
@ -681,6 +683,16 @@ func (s *k8sStore) ListVirtualService() (l7vs []*v1.VirtualService, l4vs []*v1.V
Path: path.Path,
NameCondition: map[string]*v1.Condition{},
}
i, err := rewrite.NewParser(s).Parse(&ing.ObjectMeta)
if err == nil {
if cfg, ok := i.(*rewrite.Config); ok {
location.Rewrite.Rewrites = cfg.Rewrites
}
}
pathRewrite, _ := parser.GetBoolAnnotation("path-rewrite", &ing.ObjectMeta)
if pathRewrite {
location.PathRewrite = true
}
srvLocMap[locKey] = location
vs.Locations = append(vs.Locations, location)
// the first ingress proxy takes effect
@ -782,6 +794,17 @@ func (s *k8sStore) ListVirtualService() (l7vs []*v1.VirtualService, l4vs []*v1.V
location = &v1.Location{
Path: path.Path,
NameCondition: map[string]*v1.Condition{},
Rewrite: rewrite.Config{},
}
i, err := rewrite.NewParser(s).Parse(&ing.ObjectMeta)
if err == nil {
if cfg, ok := i.(*rewrite.Config); ok {
location.Rewrite.Rewrites = cfg.Rewrites
}
}
pathRewrite, _ := parser.GetBoolAnnotation("path-rewrite", &ing.ObjectMeta)
if pathRewrite {
location.PathRewrite = true
}
srvLocMap[locKey] = location
vs.Locations = append(vs.Locations, location)

View File

@ -47,6 +47,7 @@ type Location struct {
// +optional
Proxy proxy.Config `json:"proxy,omitempty"`
DisableProxyPass bool
PathRewrite bool `json:"pathRewrite"`
}
// Condition is the condition that the traffic can reach the specified backend
@ -80,6 +81,13 @@ func (l *Location) Equals(c *Location) bool {
return false
}
if !l.Rewrite.Equal(&c.Rewrite) {
return false
}
if l.PathRewrite != c.PathRewrite {
return false
}
return true
}

View File

@ -540,6 +540,20 @@ func (a *AppServiceBuild) parseAnnotations(rule *model.HTTPRule) (map[string]str
annos[parser.GetAnnotationWithPrefix("cookie")] = rule.Cookie
}
// path-rewrite
if rule.PathRewrite {
annos[parser.GetAnnotationWithPrefix("path-rewrite")] = "true"
}
httpRuleRewrites, err := a.dbmanager.HTTPRuleRewriteDao().ListByHTTPRuleID(rule.UUID)
if err != nil {
return nil, err
}
for i, rewrite := range httpRuleRewrites {
annos[parser.GetAnnotationWithPrefix(fmt.Sprintf("rewrite-%d-regex", i))] = rewrite.Regex
annos[parser.GetAnnotationWithPrefix(fmt.Sprintf("rewrite-%d-replacement", i))] = rewrite.Replacement
annos[parser.GetAnnotationWithPrefix(fmt.Sprintf("rewrite-%d-flag", i))] = rewrite.Flag
}
// rule extension
ruleExtensions, err := a.dbmanager.RuleExtensionDao().GetRuleExtensionByRuleID(rule.UUID)
if err != nil {