blob: 12f7161057c0a2b87ed21c798c2f39a8434c6e45 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 (
"context"
"encoding/json"
"github.com/apache/servicecomb-service-center/pkg/log"
"github.com/apache/servicecomb-service-center/pkg/util"
apt "github.com/apache/servicecomb-service-center/server/core"
"github.com/apache/servicecomb-service-center/server/core/backend"
pb "github.com/apache/servicecomb-service-center/server/core/proto"
"github.com/apache/servicecomb-service-center/server/plugin"
"github.com/apache/servicecomb-service-center/server/plugin/quota"
"github.com/apache/servicecomb-service-center/server/plugin/registry"
scerr "github.com/apache/servicecomb-service-center/server/scerror"
serviceUtil "github.com/apache/servicecomb-service-center/server/service/util"
"strconv"
"time"
)
func (s *MicroServiceService) AddRule(ctx context.Context, in *pb.AddServiceRulesRequest) (*pb.AddServiceRulesResponse, error) {
remoteIP := util.GetIPFromContext(ctx)
err := Validate(in)
if err != nil {
log.Errorf(err, "add service[%s] rule failed, operator: %s", in.ServiceId, remoteIP)
return &pb.AddServiceRulesResponse{
Response: pb.CreateResponse(scerr.ErrInvalidParams, err.Error()),
}, nil
}
domainProject := util.ParseDomainProject(ctx)
// service id存在性校验
if !serviceUtil.ServiceExist(ctx, domainProject, in.ServiceId) {
log.Errorf(nil, "add service[%s] rule failed, service does not exist, operator: %s",
in.ServiceId, remoteIP)
return &pb.AddServiceRulesResponse{
Response: pb.CreateResponse(scerr.ErrInvalidParams, "Service does not exist."),
}, nil
}
res := quota.NewApplyQuotaResource(quota.RuleQuotaType, domainProject, in.ServiceId, int64(len(in.Rules)))
rst := plugin.Plugins().Quota().Apply4Quotas(ctx, res)
errQuota := rst.Err
if errQuota != nil {
log.Errorf(errQuota, "add service[%s] rule failed, operator: %s", in.ServiceId, remoteIP)
response := &pb.AddServiceRulesResponse{
Response: pb.CreateResponseWithSCErr(errQuota),
}
if errQuota.InternalError() {
return response, errQuota
}
return response, nil
}
ruleType, _, err := serviceUtil.GetServiceRuleType(ctx, domainProject, in.ServiceId)
if err != nil {
return &pb.AddServiceRulesResponse{
Response: pb.CreateResponse(scerr.ErrInternal, err.Error()),
}, err
}
ruleIDs := make([]string, 0, len(in.Rules))
opts := make([]registry.PluginOp, 0, 2*len(in.Rules))
for _, rule := range in.Rules {
//黑白名单只能存在一种,黑名单 or 白名单
if len(ruleType) == 0 {
ruleType = rule.RuleType
} else if ruleType != rule.RuleType {
log.Errorf(nil, "add service[%s] rule failed, can not add different RuleType at the same time, operator: %s",
in.ServiceId, remoteIP)
return &pb.AddServiceRulesResponse{
Response: pb.CreateResponse(scerr.ErrBlackAndWhiteRule, "Service can only contain one rule type, BLACK or WHITE."),
}, nil
}
//同一服务,attribute和pattern确定一个rule
if serviceUtil.RuleExist(ctx, domainProject, in.ServiceId, rule.Attribute, rule.Pattern) {
log.Infof("service[%s] rule[%s/%s] already exists, operator: %s",
in.ServiceId, rule.Attribute, rule.Pattern, remoteIP)
continue
}
// 产生全局rule id
timestamp := strconv.FormatInt(time.Now().Unix(), 10)
ruleAdd := &pb.ServiceRule{
RuleId: util.GenerateUUID(),
RuleType: rule.RuleType,
Attribute: rule.Attribute,
Pattern: rule.Pattern,
Description: rule.Description,
Timestamp: timestamp,
ModTimestamp: timestamp,
}
key := apt.GenerateServiceRuleKey(domainProject, in.ServiceId, ruleAdd.RuleId)
indexKey := apt.GenerateRuleIndexKey(domainProject, in.ServiceId, ruleAdd.Attribute, ruleAdd.Pattern)
ruleIDs = append(ruleIDs, ruleAdd.RuleId)
data, err := json.Marshal(ruleAdd)
if err != nil {
log.Errorf(err, "add service[%s] rule failed, marshal rule[%s/%s] failed, operator: %s",
in.ServiceId, ruleAdd.Attribute, ruleAdd.Pattern, remoteIP)
return &pb.AddServiceRulesResponse{
Response: pb.CreateResponse(scerr.ErrInternal, err.Error()),
}, err
}
opts = append(opts, registry.OpPut(registry.WithStrKey(key), registry.WithValue(data)))
opts = append(opts, registry.OpPut(registry.WithStrKey(indexKey), registry.WithStrValue(ruleAdd.RuleId)))
}
if len(opts) <= 0 {
log.Infof("add service[%s] rule successfully, no rules to add, operator: %s",
in.ServiceId, remoteIP)
return &pb.AddServiceRulesResponse{
Response: pb.CreateResponse(pb.Response_SUCCESS, "Service rules has been added."),
}, nil
}
resp, err := backend.BatchCommitWithCmp(ctx, opts,
[]registry.CompareOp{registry.OpCmp(
registry.CmpVer(util.StringToBytesWithNoCopy(apt.GenerateServiceKey(domainProject, in.ServiceId))),
registry.CmpNotEqual, 0)},
nil)
if err != nil {
log.Errorf(err, "add service[%s] rule failed, operator: %s", in.ServiceId, remoteIP)
return &pb.AddServiceRulesResponse{
Response: pb.CreateResponse(scerr.ErrUnavailableBackend, err.Error()),
}, err
}
if !resp.Succeeded {
log.Errorf(nil, "add service[%s] rule failed, service does not exist, operator: %s",
in.ServiceId, remoteIP)
return &pb.AddServiceRulesResponse{
Response: pb.CreateResponse(scerr.ErrServiceNotExists, "Service does not exist."),
}, nil
}
log.Infof("add service[%s] rule %v successfully, operator: %s", in.ServiceId, ruleIDs, remoteIP)
return &pb.AddServiceRulesResponse{
Response: pb.CreateResponse(pb.Response_SUCCESS, "Add service rules successfully."),
RuleIds: ruleIDs,
}, nil
}
func (s *MicroServiceService) UpdateRule(ctx context.Context, in *pb.UpdateServiceRuleRequest) (*pb.UpdateServiceRuleResponse, error) {
remoteIP := util.GetIPFromContext(ctx)
err := Validate(in)
if err != nil {
log.Errorf(err, "update service rule[%s/%s] failed, operator: %s", in.ServiceId, in.RuleId, remoteIP)
return &pb.UpdateServiceRuleResponse{
Response: pb.CreateResponse(scerr.ErrInvalidParams, err.Error()),
}, nil
}
domainProject := util.ParseDomainProject(ctx)
// service id存在性校验
if !serviceUtil.ServiceExist(ctx, domainProject, in.ServiceId) {
log.Errorf(nil, "update service rule[%s/%s] failed, service does not exist, operator: %s",
in.ServiceId, in.RuleId, remoteIP)
return &pb.UpdateServiceRuleResponse{
Response: pb.CreateResponse(scerr.ErrServiceNotExists, "Service does not exist."),
}, nil
}
//是否能改变ruleType
ruleType, ruleNum, err := serviceUtil.GetServiceRuleType(ctx, domainProject, in.ServiceId)
if err != nil {
log.Errorf(err, "update service rule[%s/%s] failed, get rule type failed, operator: %s",
in.ServiceId, in.RuleId, remoteIP)
return &pb.UpdateServiceRuleResponse{
Response: pb.CreateResponse(scerr.ErrInternal, err.Error()),
}, err
}
if ruleNum >= 1 && ruleType != in.Rule.RuleType {
log.Errorf(err, "update service rule[%s/%s] failed, can only exist one type, current type is %s, operator: %s",
in.ServiceId, in.RuleId, ruleType, remoteIP)
return &pb.UpdateServiceRuleResponse{
Response: pb.CreateResponse(scerr.ErrModifyRuleNotAllow, "Exist multiple rules,can not change rule type. Rule type is "+ruleType),
}, nil
}
rule, err := serviceUtil.GetOneRule(ctx, domainProject, in.ServiceId, in.RuleId)
if err != nil {
log.Errorf(err, "update service rule[%s/%s] failed, query service rule failed, operator: %s",
in.ServiceId, in.RuleId, remoteIP)
return &pb.UpdateServiceRuleResponse{
Response: pb.CreateResponse(scerr.ErrInternal, err.Error()),
}, err
}
if rule == nil {
log.Errorf(err, "update service rule[%s/%s] failed, service rule does not exist, operator: %s",
in.ServiceId, in.RuleId, remoteIP)
return &pb.UpdateServiceRuleResponse{
Response: pb.CreateResponse(scerr.ErrRuleNotExists, "This rule does not exist."),
}, nil
}
copyRuleRef := *rule
oldRulePatten := copyRuleRef.Pattern
oldRuleAttr := copyRuleRef.Attribute
isChangeIndex := false
if copyRuleRef.Attribute != in.Rule.Attribute {
isChangeIndex = true
copyRuleRef.Attribute = in.Rule.Attribute
}
if copyRuleRef.Pattern != in.Rule.Pattern {
isChangeIndex = true
copyRuleRef.Pattern = in.Rule.Pattern
}
copyRuleRef.RuleType = in.Rule.RuleType
copyRuleRef.Description = in.Rule.Description
copyRuleRef.ModTimestamp = strconv.FormatInt(time.Now().Unix(), 10)
key := apt.GenerateServiceRuleKey(domainProject, in.ServiceId, in.RuleId)
data, err := json.Marshal(copyRuleRef)
if err != nil {
log.Errorf(err, "update service rule[%s/%s] failed, marshal service rule failed, operator: %s",
in.ServiceId, in.RuleId, remoteIP)
return &pb.UpdateServiceRuleResponse{
Response: pb.CreateResponse(scerr.ErrInternal, err.Error()),
}, err
}
var opts []registry.PluginOp
if isChangeIndex {
//加入新的rule index
indexKey := apt.GenerateRuleIndexKey(domainProject, in.ServiceId, copyRuleRef.Attribute, copyRuleRef.Pattern)
opts = append(opts, registry.OpPut(registry.WithStrKey(indexKey), registry.WithStrValue(copyRuleRef.RuleId)))
//删除旧的rule index
oldIndexKey := apt.GenerateRuleIndexKey(domainProject, in.ServiceId, oldRuleAttr, oldRulePatten)
opts = append(opts, registry.OpDel(registry.WithStrKey(oldIndexKey)))
}
opts = append(opts, registry.OpPut(registry.WithStrKey(key), registry.WithValue(data)))
resp, err := backend.Registry().TxnWithCmp(ctx, opts,
[]registry.CompareOp{registry.OpCmp(
registry.CmpVer(util.StringToBytesWithNoCopy(apt.GenerateServiceKey(domainProject, in.ServiceId))),
registry.CmpNotEqual, 0)},
nil)
if err != nil {
log.Errorf(err, "update service rule[%s/%s] failed, operator: %s", in.ServiceId, in.RuleId, remoteIP)
return &pb.UpdateServiceRuleResponse{
Response: pb.CreateResponse(scerr.ErrUnavailableBackend, err.Error()),
}, err
}
if !resp.Succeeded {
log.Errorf(err, "update service rule[%s/%s] failed, service does not exist, operator: %s",
in.ServiceId, in.RuleId, remoteIP)
return &pb.UpdateServiceRuleResponse{
Response: pb.CreateResponse(scerr.ErrServiceNotExists, "Service does not exist."),
}, nil
}
log.Infof("update service rule[%s/%s] successfully, operator: %s", in.ServiceId, in.RuleId, remoteIP)
return &pb.UpdateServiceRuleResponse{
Response: pb.CreateResponse(pb.Response_SUCCESS, "Get service rules successfully."),
}, nil
}
func (s *MicroServiceService) GetRule(ctx context.Context, in *pb.GetServiceRulesRequest) (*pb.GetServiceRulesResponse, error) {
err := Validate(in)
if err != nil {
log.Errorf(err, "get service[%s] rule failed", in.ServiceId)
return &pb.GetServiceRulesResponse{
Response: pb.CreateResponse(scerr.ErrInvalidParams, err.Error()),
}, nil
}
domainProject := util.ParseDomainProject(ctx)
// service id存在性校验
if !serviceUtil.ServiceExist(ctx, domainProject, in.ServiceId) {
log.Errorf(nil, "get service[%s] rule failed, service does not exist", in.ServiceId)
return &pb.GetServiceRulesResponse{
Response: pb.CreateResponse(scerr.ErrServiceNotExists, "Service does not exist."),
}, nil
}
rules, err := serviceUtil.GetRulesUtil(ctx, domainProject, in.ServiceId)
if err != nil {
log.Errorf(err, "get service[%s] rule failed", in.ServiceId)
return &pb.GetServiceRulesResponse{
Response: pb.CreateResponse(scerr.ErrInternal, err.Error()),
}, err
}
return &pb.GetServiceRulesResponse{
Response: pb.CreateResponse(pb.Response_SUCCESS, "Get service rules successfully."),
Rules: rules,
}, nil
}
func (s *MicroServiceService) DeleteRule(ctx context.Context, in *pb.DeleteServiceRulesRequest) (*pb.DeleteServiceRulesResponse, error) {
remoteIP := util.GetIPFromContext(ctx)
err := Validate(in)
if err != nil {
log.Errorf(err, "delete service[%s] rules %v failed, operator: %s", in.ServiceId, in.RuleIds, remoteIP)
return &pb.DeleteServiceRulesResponse{
Response: pb.CreateResponse(scerr.ErrInvalidParams, err.Error()),
}, nil
}
domainProject := util.ParseDomainProject(ctx)
// service id存在性校验
if !serviceUtil.ServiceExist(ctx, domainProject, in.ServiceId) {
log.Errorf(nil, "delete service[%s] rules %v failed, service does not exist, operator: %s",
in.ServiceId, in.RuleIds, remoteIP)
return &pb.DeleteServiceRulesResponse{
Response: pb.CreateResponse(scerr.ErrServiceNotExists, "Service does not exist."),
}, nil
}
opts := []registry.PluginOp{}
key := ""
indexKey := ""
for _, ruleID := range in.RuleIds {
key = apt.GenerateServiceRuleKey(domainProject, in.ServiceId, ruleID)
log.Debugf("start delete service rule file: %s", key)
data, err := serviceUtil.GetOneRule(ctx, domainProject, in.ServiceId, ruleID)
if err != nil {
log.Errorf(err, "delete service[%s] rules %v failed, get rule[%s] failed, operator: %s",
in.ServiceId, in.RuleIds, ruleID, remoteIP)
return &pb.DeleteServiceRulesResponse{
Response: pb.CreateResponse(scerr.ErrInternal, err.Error()),
}, err
}
if data == nil {
log.Errorf(nil, "delete service[%s] rules %v failed, rule[%s] does not exist, operator: %s",
in.ServiceId, in.RuleIds, ruleID, remoteIP)
return &pb.DeleteServiceRulesResponse{
Response: pb.CreateResponse(scerr.ErrRuleNotExists, "This rule does not exist."),
}, nil
}
indexKey = apt.GenerateRuleIndexKey(domainProject, in.ServiceId, data.Attribute, data.Pattern)
opts = append(opts,
registry.OpDel(registry.WithStrKey(key)),
registry.OpDel(registry.WithStrKey(indexKey)))
}
if len(opts) <= 0 {
log.Errorf(nil, "delete service[%s] rules %v failed, no rule has been deleted, operator: %s",
in.ServiceId, in.RuleIds, remoteIP)
return &pb.DeleteServiceRulesResponse{
Response: pb.CreateResponse(scerr.ErrRuleNotExists, "No service rule has been deleted."),
}, nil
}
resp, err := backend.BatchCommitWithCmp(ctx, opts,
[]registry.CompareOp{registry.OpCmp(
registry.CmpVer(util.StringToBytesWithNoCopy(apt.GenerateServiceKey(domainProject, in.ServiceId))),
registry.CmpNotEqual, 0)},
nil)
if err != nil {
log.Errorf(err, "delete service[%s] rules %v failed, operator: %s", in.ServiceId, in.RuleIds, remoteIP)
return &pb.DeleteServiceRulesResponse{
Response: pb.CreateResponse(scerr.ErrUnavailableBackend, err.Error()),
}, err
}
if !resp.Succeeded {
log.Errorf(err, "delete service[%s] rules %v failed, service does not exist, operator: %s",
in.ServiceId, in.RuleIds, remoteIP)
return &pb.DeleteServiceRulesResponse{
Response: pb.CreateResponse(scerr.ErrServiceNotExists, "Service does not exist."),
}, nil
}
log.Infof("delete service[%s] rules %v successfully, operator: %s", in.ServiceId, in.RuleIds, remoteIP)
return &pb.DeleteServiceRulesResponse{
Response: pb.CreateResponse(pb.Response_SUCCESS, "Delete service rules successfully."),
}, nil
}