server/service/grc/kie/kie_distributor.go (313 lines of code) (raw):

/* * 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 kie import ( "context" "encoding/json" "fmt" "math/rand" "strings" "time" "github.com/ghodss/yaml" "github.com/go-chassis/foundation/httpclient" "github.com/go-chassis/kie-client" "github.com/apache/servicecomb-service-center/pkg/util" "github.com/apache/servicecomb-service-center/pkg/gov" "github.com/apache/servicecomb-service-center/pkg/log" "github.com/apache/servicecomb-service-center/server/config" grcsvc "github.com/apache/servicecomb-service-center/server/service/grc" rbacsvc "github.com/apache/servicecomb-service-center/server/service/rbac" ) const Priority = -1 type Distributor struct { name string client *kie.Client } func (d *Distributor) Create(ctx context.Context, kind, project string, p *gov.Policy) ([]byte, error) { if kind == grcsvc.KindMatchGroup { err := d.generateID(ctx, project, p) if err != nil { return nil, err } } if kind == grcsvc.KindMatchGroup { setAliasIfEmpty(p.Spec, p.Name) } yamlByte, err := yaml.Marshal(p.Spec) if err != nil { return nil, err } kv := kie.KVRequest{ Key: toGovKeyPrefix(kind) + p.Name, Value: string(yamlByte), Status: grcsvc.StatusEnabled, ValueType: grcsvc.TypeText, Priority: Priority, Labels: p.Selector, } res, err := d.client.Create(ctx, kv, kie.WithProject(project)) if err != nil { log.Error("kie create failed", err) return nil, err } return []byte(res.ID), nil } func (d *Distributor) Update(ctx context.Context, kind, id, project string, p *gov.Policy) error { if kind == grcsvc.KindMatchGroup { setAliasIfEmpty(p.Spec, p.Name) } yamlByte, err := yaml.Marshal(p.Spec) if err != nil { return err } kv := kie.KVRequest{ ID: id, Value: string(yamlByte), Status: p.Status, } _, err = d.client.Put(ctx, kv, kie.WithProject(project)) if err != nil { log.Error("kie update failed", err) return err } return nil } func (d *Distributor) Delete(ctx context.Context, kind, id, project string) error { if kind == grcsvc.KindMatchGroup { // should remove all policies of this group return d.DeleteMatchGroup(ctx, id, project) } err := d.client.Delete(ctx, id, kie.WithProject(project)) if err != nil { log.Error("kie delete failed", err) return err } return nil } func (d *Distributor) DeleteMatchGroup(ctx context.Context, id string, project string) error { policy, err := d.getPolicy(ctx, grcsvc.KindMatchGroup, id, project) if err != nil { log.Error("kie get failed", err) return err } ops := []kie.GetOption{ kie.WithKey("wildcard(" + grcsvc.KeyPrefix + "*." + policy.Name + ")"), kie.WithLabels(policy.Selector), kie.WithRevision(0), kie.WithGetProject(project), } idList, _, err := d.client.List(ctx, ops...) if err != nil { log.Error("kie list failed", err) return err } var ids string for _, res := range idList.Data { ids += res.ID + "," } if len(ids) == 0 { return nil } err = d.client.Delete(ctx, ids[:len(ids)-1], kie.WithProject(project)) if err != nil { log.Error("kie list failed", err) return err } return nil } func (d *Distributor) Display(ctx context.Context, project, app, env string) ([]byte, error) { list, _, err := d.listDataByKind(ctx, grcsvc.KindMatchGroup, project, app, env) if err != nil { return nil, err } policyMap := make(map[string]*gov.Policy) for _, kind := range grcsvc.PolicyNames { policies, _, err := d.listDataByKind(ctx, kind, project, app, env) if err != nil { continue } for _, policy := range policies.Data { item, err := d.transform(policy, kind) if err != nil { log.Warn(fmt.Sprintf("transform config failed: key is [%s], value is [%s]", policy.Key, policy.Value)) continue } policyMap[item.Name+kind] = item } } r := make([]*gov.DisplayData, 0, list.Total) for _, item := range list.Data { match, err := d.transform(item, grcsvc.KindMatchGroup) if err != nil { log.Warn(fmt.Sprintf("transform config failed: key is [%s], value is [%s]", item.Key, item.Value)) continue } var policies []*gov.Policy for _, kind := range grcsvc.PolicyNames { if policyMap[match.Name+kind] != nil { policies = append(policies, policyMap[match.Name+kind]) } } result := &gov.DisplayData{ Policies: policies, MatchGroup: match, } r = append(r, result) } b, _ := json.MarshalIndent(r, "", " ") return b, nil } func setAliasIfEmpty(spec map[string]interface{}, name string) { if spec["alias"] == nil { spec["alias"] = name return } alias := spec["alias"].(string) if alias == "" { spec["alias"] = name } } func (d *Distributor) List(ctx context.Context, kind, project, app, env string) ([]byte, error) { list, _, err := d.listDataByKind(ctx, kind, project, app, env) if err != nil { return nil, err } r := make([]*gov.Policy, 0, list.Total) for _, item := range list.Data { policy, err := d.transform(item, kind) if err != nil { log.Warn(fmt.Sprintf("transform config failed: key is [%s], value is [%s]", item.Key, item.Value)) continue } r = append(r, policy) } b, _ := json.MarshalIndent(r, "", " ") return b, nil } func (d *Distributor) Get(ctx context.Context, kind, id, project string) ([]byte, error) { policy, err := d.getPolicy(ctx, kind, id, project) if err != nil { return nil, err } b, _ := json.MarshalIndent(policy, "", " ") return b, nil } func (d *Distributor) getPolicy(ctx context.Context, kind string, id string, project string) (*gov.Policy, error) { kv, err := d.client.Get(ctx, id, kie.WithGetProject(project)) if err != nil { return nil, err } policy, err := d.transform(kv, kind) if err != nil { return nil, err } return policy, nil } func (d *Distributor) Type() string { return grcsvc.ConfigDistributorKie } func (d *Distributor) Name() string { return d.name } func initClient(endpoint string) *kie.Client { client, err := kie.NewClient( kie.Config{Endpoint: endpoint, DefaultLabels: map[string]string{}, HTTPOptions: &httpclient.Options{ SignRequest: rbacsvc.SignRequest, }, }) if err != nil { log.Fatal("init kie client failed, err: %s", err) } return client } func kieDistributorNew(opts config.DistributorOptions) (grcsvc.ConfigDistributor, error) { return &Distributor{name: opts.Name, client: initClient(opts.Endpoint)}, nil } func (d *Distributor) listDataByKind(ctx context.Context, kind, project, app, env string) (*kie.KVResponse, int, error) { ops := []kie.GetOption{ kie.WithKey("beginWith(" + toGovKeyPrefix(kind) + ")"), kie.WithRevision(0), kie.WithGetProject(project), } labels := map[string]string{} if env != grcsvc.EnvAll { labels[grcsvc.KeyEnvironment] = env } if app != "" { labels[grcsvc.KeyApp] = app } if len(labels) > 0 { ops = append(ops, kie.WithLabels(labels)) } list, rev, err := d.client.List(ctx, ops...) if err == kie.ErrNoChanges { list = &kie.KVResponse{} err = nil } return list, rev, err } func (d *Distributor) generateID(ctx context.Context, project string, p *gov.Policy) error { if p.Name != "" { return nil } kind := grcsvc.KindMatchGroup list, _, err := d.listDataByKind(ctx, kind, project, p.Selector[grcsvc.KeyApp], p.Selector[grcsvc.KeyEnvironment]) if err != nil { return err } var id string for { var repeat bool id = getID() govKey := toGovKeyPrefix(kind) + id for _, datum := range list.Data { if govKey == datum.Key { repeat = true break } } if !repeat { break } } p.Name = id return nil } func getID() string { str := "0123456789abcdefghijklmnopqrstuvwxyz" b := []byte(str) var result []byte r := rand.New(rand.NewSource(time.Now().UnixNano())) for i := 0; i < 4; i++ { result = append(result, b[r.Intn(len(b))]) } return grcsvc.GroupNamePrefix + string(result) } func (d *Distributor) transform(kv *kie.KVDoc, kind string) (*gov.Policy, error) { goc := &gov.Policy{ GovernancePolicy: &gov.GovernancePolicy{ Selector: gov.Selector{}, }, } spec := make(map[string]interface{}) specJSON, _ := yaml.YAMLToJSON([]byte(kv.Value)) err := json.Unmarshal(specJSON, &spec) if err != nil { log.Error("kie transform kv failed", err) return nil, err } goc.Kind = kind goc.ID = kv.ID goc.Status = kv.Status goc.Name = kv.Key[strings.LastIndex(kv.Key, ".")+1 : len(kv.Key)] goc.Spec = spec goc.Selector = kv.Labels goc.CreatTime = kv.CreatTime goc.UpdateTime = kv.UpdateTime return goc, nil } func toGovKeyPrefix(kind string) string { return grcsvc.KeyPrefix + util.ToSnake(kind) + "." } func init() { grcsvc.InstallDistributor(grcsvc.ConfigDistributorKie, kieDistributorNew) }