Files
kubesphere/vendor/github.com/projectcalico/libcalico-go/lib/backend/model/profile.go
2019-08-17 15:34:02 +08:00

234 lines
5.9 KiB
Go

// Copyright (c) 2016 Tigera, Inc. 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://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 model
import (
"fmt"
"regexp"
"reflect"
"sort"
"github.com/projectcalico/libcalico-go/lib/errors"
log "github.com/sirupsen/logrus"
)
var (
matchProfile = regexp.MustCompile("^/?calico/v1/policy/profile/([^/]+)/(tags|rules|labels)$")
typeProfile = reflect.TypeOf(Profile{})
)
// The profile key actually returns the common parent of the three separate entries.
// It is useful to define this to re-use some of the common machinery, and can be used
// for delete processing since delete needs to remove the common parent.
type ProfileKey struct {
Name string `json:"-" validate:"required,name"`
}
func (key ProfileKey) defaultPath() (string, error) {
if key.Name == "" {
return "", errors.ErrorInsufficientIdentifiers{Name: "name"}
}
e := fmt.Sprintf("/calico/v1/policy/profile/%s", escapeName(key.Name))
return e, nil
}
func (key ProfileKey) defaultDeletePath() (string, error) {
return key.defaultPath()
}
func (key ProfileKey) defaultDeleteParentPaths() ([]string, error) {
return nil, nil
}
func (key ProfileKey) valueType() (reflect.Type, error) {
return typeProfile, nil
}
func (key ProfileKey) String() string {
return fmt.Sprintf("Profile(name=%s)", key.Name)
}
// ProfileRulesKey implements the KeyInterface for the profile rules
type ProfileRulesKey struct {
ProfileKey
}
func (key ProfileRulesKey) defaultPath() (string, error) {
e, err := key.ProfileKey.defaultPath()
return e + "/rules", err
}
func (key ProfileRulesKey) valueType() (reflect.Type, error) {
return reflect.TypeOf(ProfileRules{}), nil
}
func (key ProfileRulesKey) String() string {
return fmt.Sprintf("ProfileRules(name=%s)", key.Name)
}
// ProfileTagsKey implements the KeyInterface for the profile tags
type ProfileTagsKey struct {
ProfileKey
}
func (key ProfileTagsKey) defaultPath() (string, error) {
e, err := key.ProfileKey.defaultPath()
return e + "/tags", err
}
func (key ProfileTagsKey) valueType() (reflect.Type, error) {
return reflect.TypeOf([]string{}), nil
}
func (key ProfileTagsKey) String() string {
return fmt.Sprintf("ProfileTags(name=%s)", key.Name)
}
// ProfileLabelsKey implements the KeyInterface for the profile labels
type ProfileLabelsKey struct {
ProfileKey
}
func (key ProfileLabelsKey) defaultPath() (string, error) {
e, err := key.ProfileKey.defaultPath()
return e + "/labels", err
}
func (key ProfileLabelsKey) valueType() (reflect.Type, error) {
return reflect.TypeOf(map[string]string{}), nil
}
func (key ProfileLabelsKey) String() string {
return fmt.Sprintf("ProfileLabels(name=%s)", key.Name)
}
type ProfileListOptions struct {
Name string
}
func (options ProfileListOptions) defaultPathRoot() string {
k := "/calico/v1/policy/profile"
if options.Name == "" {
return k
}
k = k + fmt.Sprintf("/%s", escapeName(options.Name))
return k
}
func (options ProfileListOptions) KeyFromDefaultPath(path string) Key {
log.Debugf("Get Profile key from %s", path)
r := matchProfile.FindAllStringSubmatch(path, -1)
if len(r) != 1 {
log.Debugf("Didn't match regex")
return nil
}
name := unescapeName(r[0][1])
kind := r[0][2]
if options.Name != "" && name != options.Name {
log.Debugf("Didn't match name %s != %s", options.Name, name)
return nil
}
pk := ProfileKey{Name: name}
switch kind {
case "tags":
return ProfileTagsKey{ProfileKey: pk}
case "labels":
return ProfileLabelsKey{ProfileKey: pk}
case "rules":
return ProfileRulesKey{ProfileKey: pk}
}
return pk
}
// The profile structure is defined to allow the client to define a conversion interface
// to map between the API and backend profiles. However, in the actual underlying
// implementation the profile is written as three separate entries - rules, tags and labels.
type Profile struct {
Rules ProfileRules
Tags []string
Labels map[string]string
}
type ProfileRules struct {
InboundRules []Rule `json:"inbound_rules,omitempty" validate:"omitempty,dive"`
OutboundRules []Rule `json:"outbound_rules,omitempty" validate:"omitempty,dive"`
}
func (_ *ProfileListOptions) ListConvert(ds []*KVPair) []*KVPair {
profiles := make(map[string]*KVPair)
var name string
for _, d := range ds {
switch t := d.Key.(type) {
case ProfileTagsKey:
name = t.Name
case ProfileLabelsKey:
name = t.Name
case ProfileRulesKey:
name = t.Name
default:
panic(fmt.Errorf("Unexpected key type: %v", t))
}
// Get the KVPair for the profile, initialising if just created.
pd, ok := profiles[name]
if !ok {
log.Debugf("Initialise profile %v", name)
pd = &KVPair{
Value: &Profile{},
Key: ProfileKey{Name: name},
}
profiles[name] = pd
}
p := pd.Value.(*Profile)
switch t := d.Value.(type) {
case []string: // must be tags #TODO should type these
log.Debugf("Store tags %v", t)
p.Tags = t
pd.Revision = d.Revision
case map[string]string: // must be labels
log.Debugf("Store labels %v", t)
p.Labels = t
case *ProfileRules: // must be rules
log.Debugf("Store rules %v", t)
p.Rules = *t
default:
panic(fmt.Errorf("Unexpected type: %v", t))
}
pd.Value = p
}
log.Debugf("Map of profiles: %v", profiles)
// To store the keys in slice in sorted order
var keys []string
for k := range profiles {
keys = append(keys, k)
}
sort.Strings(keys)
out := make([]*KVPair, len(keys))
for i, k := range keys {
out[i] = profiles[k]
}
log.Debugf("Sorted groups of profiles: %v", out)
return out
}