gomod: change projectcalico/calico to kubesphere/calico (#5557)

* chore(calico): update calico to 3.25.0

* chore(calico): replace projectcalico/calico to kubesphere/calico

Signed-off-by: root <renyunkang@kubesphere.io>

---------

Signed-off-by: root <renyunkang@kubesphere.io>
This commit is contained in:
Yunkang Ren
2023-02-28 17:03:36 +08:00
committed by GitHub
parent dc28a0917a
commit a3a6a1cd98
146 changed files with 11189 additions and 4663 deletions

View File

@@ -0,0 +1,58 @@
// Copyright (c) 2017 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"
"reflect"
"github.com/projectcalico/calico/libcalico-go/lib/errors"
)
var (
typeBGPNode = reflect.TypeOf(BGPNode{})
)
type BGPNodeKey struct {
Host string
}
func (key BGPNodeKey) defaultPath() (string, error) {
if key.Host == "" {
return "", errors.ErrorInsufficientIdentifiers{Name: "host"}
}
k := "/calico/bgp/v1/host/" + key.Host
return k, nil
}
func (key BGPNodeKey) defaultDeletePath() (string, error) {
return key.defaultPath()
}
func (key BGPNodeKey) defaultDeleteParentPaths() ([]string, error) {
return nil, nil
}
func (key BGPNodeKey) valueType() (reflect.Type, error) {
return typeBGPNode, nil
}
func (key BGPNodeKey) String() string {
return fmt.Sprintf("BGPNodeKey(host=%s)", key.Host)
}
type BGPNode struct {
}

View File

@@ -0,0 +1,162 @@
// 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"
"reflect"
"regexp"
log "github.com/sirupsen/logrus"
"github.com/projectcalico/calico/libcalico-go/lib/errors"
)
var (
matchGlobalBGPConfig = regexp.MustCompile("^/?calico/bgp/v1/global/(.+)$")
matchNodeBGPConfig = regexp.MustCompile("^/?calico/bgp/v1/host/([^/]+)/(.+)$")
typeGlobalBGPConfig = rawStringType
typeNodeBGPConfig = rawStringType
)
type GlobalBGPConfigKey struct {
// The name of the global BGP config key.
Name string `json:"-" validate:"required,name"`
}
func (key GlobalBGPConfigKey) defaultPath() (string, error) {
return key.defaultDeletePath()
}
func (key GlobalBGPConfigKey) defaultDeletePath() (string, error) {
if key.Name == "" {
return "", errors.ErrorInsufficientIdentifiers{Name: "name"}
}
e := fmt.Sprintf("/calico/bgp/v1/global/%s", key.Name)
return e, nil
}
func (key GlobalBGPConfigKey) defaultDeleteParentPaths() ([]string, error) {
return nil, nil
}
func (key GlobalBGPConfigKey) valueType() (reflect.Type, error) {
return typeGlobalBGPConfig, nil
}
func (key GlobalBGPConfigKey) String() string {
return fmt.Sprintf("GlobalBGPConfig(name=%s)", key.Name)
}
type GlobalBGPConfigListOptions struct {
Name string
}
func (options GlobalBGPConfigListOptions) defaultPathRoot() string {
k := "/calico/bgp/v1/global"
if options.Name == "" {
return k
}
k = k + fmt.Sprintf("/%s", options.Name)
return k
}
func (options GlobalBGPConfigListOptions) KeyFromDefaultPath(path string) Key {
log.Debugf("Get GlobalFelixConfig key from %s", path)
r := matchGlobalBGPConfig.FindAllStringSubmatch(path, -1)
if len(r) != 1 {
log.Debugf("Didn't match regex")
return nil
}
name := r[0][1]
if options.Name != "" && name != options.Name {
log.Debugf("Didn't match name %s != %s", options.Name, name)
return nil
}
return GlobalBGPConfigKey{Name: name}
}
type NodeBGPConfigKey struct {
// The hostname for the host specific BGP config
Nodename string `json:"-" validate:"required,name"`
// The name of the host specific BGP config key.
Name string `json:"-" validate:"required,name"`
}
func (key NodeBGPConfigKey) defaultPath() (string, error) {
return key.defaultDeletePath()
}
func (key NodeBGPConfigKey) defaultDeletePath() (string, error) {
if key.Nodename == "" {
return "", errors.ErrorInsufficientIdentifiers{Name: "node"}
}
if key.Name == "" {
return "", errors.ErrorInsufficientIdentifiers{Name: "name"}
}
e := fmt.Sprintf("/calico/bgp/v1/host/%s/%s", key.Nodename, key.Name)
return e, nil
}
func (key NodeBGPConfigKey) defaultDeleteParentPaths() ([]string, error) {
return nil, nil
}
func (key NodeBGPConfigKey) valueType() (reflect.Type, error) {
return typeNodeBGPConfig, nil
}
func (key NodeBGPConfigKey) String() string {
return fmt.Sprintf("HostBGPConfig(node=%s; name=%s)", key.Nodename, key.Name)
}
type NodeBGPConfigListOptions struct {
Nodename string
Name string
}
func (options NodeBGPConfigListOptions) defaultPathRoot() string {
k := "/calico/bgp/v1/host/%s"
if options.Nodename == "" {
return k
}
k = k + fmt.Sprintf("/%s", options.Nodename)
if options.Name == "" {
return k
}
k = k + fmt.Sprintf("/%s", options.Name)
return k
}
func (options NodeBGPConfigListOptions) KeyFromDefaultPath(path string) Key {
log.Debugf("Get HostConfig key from %s", path)
r := matchNodeBGPConfig.FindAllStringSubmatch(path, -1)
if len(r) != 1 {
log.Debugf("Didn't match regex")
return nil
}
nodename := r[0][1]
name := r[0][2]
if options.Nodename != "" && nodename != options.Nodename {
log.Debugf("Didn't match nodename %s != %s", options.Nodename, nodename)
return nil
}
if options.Name != "" && name != options.Name {
log.Debugf("Didn't match name %s != %s", options.Name, name)
return nil
}
return NodeBGPConfigKey{Nodename: nodename, Name: name}
}

View File

@@ -0,0 +1,230 @@
// Copyright (c) 2020 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"
"reflect"
"regexp"
"strconv"
"strings"
log "github.com/sirupsen/logrus"
"github.com/projectcalico/api/pkg/lib/numorstring"
"github.com/projectcalico/calico/libcalico-go/lib/errors"
"github.com/projectcalico/calico/libcalico-go/lib/net"
)
var (
matchGlobalBGPPeer = regexp.MustCompile("^/?calico/bgp/v1/global/peer_v./([^/]+)$")
matchHostBGPPeer = regexp.MustCompile("^/?calico/bgp/v1/host/([^/]+)/peer_v./([^/]+)$")
typeBGPPeer = reflect.TypeOf(BGPPeer{})
ipPortSeparator = "-"
defaultPort uint16 = 179
)
type NodeBGPPeerKey struct {
Nodename string `json:"-" validate:"omitempty"`
PeerIP net.IP `json:"-" validate:"required"`
Port uint16 `json:"-" validate:"omitempty"`
}
func (key NodeBGPPeerKey) defaultPath() (string, error) {
if key.PeerIP.IP == nil {
return "", errors.ErrorInsufficientIdentifiers{Name: "peerIP"}
}
if key.Nodename == "" {
return "", errors.ErrorInsufficientIdentifiers{Name: "node"}
}
e := fmt.Sprintf("/calico/bgp/v1/host/%s/peer_v%d/%s",
key.Nodename, key.PeerIP.Version(), combineIPAndPort(key.PeerIP, key.Port))
return e, nil
}
func (key NodeBGPPeerKey) defaultDeletePath() (string, error) {
return key.defaultPath()
}
func (key NodeBGPPeerKey) defaultDeleteParentPaths() ([]string, error) {
return nil, nil
}
func (key NodeBGPPeerKey) valueType() (reflect.Type, error) {
return typeBGPPeer, nil
}
func (key NodeBGPPeerKey) String() string {
return fmt.Sprintf("BGPPeer(node=%s, ip=%s, port=%d)", key.Nodename, key.PeerIP, key.Port)
}
type NodeBGPPeerListOptions struct {
Nodename string
PeerIP net.IP
Port uint16
}
func (options NodeBGPPeerListOptions) defaultPathRoot() string {
if options.Nodename == "" {
return "/calico/bgp/v1/host"
} else if options.PeerIP.IP == nil {
return fmt.Sprintf("/calico/bgp/v1/host/%s",
options.Nodename)
} else {
return fmt.Sprintf("/calico/bgp/v1/host/%s/peer_v%d/%s",
options.Nodename, options.PeerIP.Version(), combineIPAndPort(options.PeerIP, options.Port))
}
}
func (options NodeBGPPeerListOptions) KeyFromDefaultPath(path string) Key {
log.Debugf("Get BGPPeer key from %s", path)
nodename := ""
var port uint16
peerIP := net.IP{}
ekeyb := []byte(path)
if r := matchHostBGPPeer.FindAllSubmatch(ekeyb, -1); len(r) == 1 {
var ipBytes []byte
ipBytes, port = extractIPAndPort(string(r[0][2]))
nodename = string(r[0][1])
if err := peerIP.UnmarshalText(ipBytes); err != nil {
log.WithError(err).WithField("PeerIP", r[0][2]).Error("Error unmarshalling GlobalBGPPeer IP address")
return nil
}
} else {
log.Debugf("%s didn't match regex", path)
return nil
}
if options.PeerIP.IP != nil && !options.PeerIP.Equal(peerIP.IP) {
log.Debugf("Didn't match peerIP %s != %s", options.PeerIP.String(), peerIP.String())
return nil
}
if options.Nodename != "" && nodename != options.Nodename {
log.Debugf("Didn't match hostname %s != %s", options.Nodename, nodename)
return nil
}
if port == 0 {
return NodeBGPPeerKey{PeerIP: peerIP, Nodename: nodename}
}
return NodeBGPPeerKey{PeerIP: peerIP, Nodename: nodename, Port: port}
}
type GlobalBGPPeerKey struct {
PeerIP net.IP `json:"-" validate:"required"`
Port uint16 `json:"-" validate:"omitempty"`
}
func (key GlobalBGPPeerKey) defaultPath() (string, error) {
if key.PeerIP.IP == nil {
return "", errors.ErrorInsufficientIdentifiers{Name: "peerIP"}
}
e := fmt.Sprintf("/calico/bgp/v1/global/peer_v%d/%s",
key.PeerIP.Version(), combineIPAndPort(key.PeerIP, key.Port))
return e, nil
}
func (key GlobalBGPPeerKey) defaultDeletePath() (string, error) {
return key.defaultPath()
}
func (key GlobalBGPPeerKey) defaultDeleteParentPaths() ([]string, error) {
return nil, nil
}
func (key GlobalBGPPeerKey) valueType() (reflect.Type, error) {
return typeBGPPeer, nil
}
func (key GlobalBGPPeerKey) String() string {
return fmt.Sprintf("BGPPeer(global, ip=%s, port=%d)", key.PeerIP, key.Port)
}
type GlobalBGPPeerListOptions struct {
PeerIP net.IP
Port uint16
}
func (options GlobalBGPPeerListOptions) defaultPathRoot() string {
if options.PeerIP.IP == nil {
return "/calico/bgp/v1/global"
} else {
return fmt.Sprintf("/calico/bgp/v1/global/peer_v%d/%s",
options.PeerIP.Version(), combineIPAndPort(options.PeerIP, options.Port))
}
}
func (options GlobalBGPPeerListOptions) KeyFromDefaultPath(path string) Key {
log.Debugf("Get BGPPeer key from %s", path)
peerIP := net.IP{}
ekeyb := []byte(path)
var port uint16
if r := matchGlobalBGPPeer.FindAllSubmatch(ekeyb, -1); len(r) == 1 {
var ipBytes []byte
ipBytes, port = extractIPAndPort(string(r[0][1]))
if err := peerIP.UnmarshalText(ipBytes); err != nil {
log.WithError(err).WithField("PeerIP", r[0][1]).Error("Error unmarshalling GlobalBGPPeer IP address")
return nil
}
} else {
log.Debugf("%s didn't match regex", path)
return nil
}
if options.PeerIP.IP != nil && !options.PeerIP.Equal(peerIP.IP) {
log.Debugf("Didn't match peerIP %s != %s", options.PeerIP.String(), peerIP.String())
return nil
}
if port == 0 {
return GlobalBGPPeerKey{PeerIP: peerIP, Port: port}
}
return GlobalBGPPeerKey{PeerIP: peerIP, Port: port}
}
type BGPPeer struct {
// PeerIP is the IP address of the BGP peer.
PeerIP net.IP `json:"ip"`
// ASNum is the AS number of the peer. Note that we write out the
// value as a string in to the backend, because confd templating
// converts large uints to float e notation which breaks the BIRD
// configuration.
ASNum numorstring.ASNumber `json:"as_num,string"`
}
func extractIPAndPort(ipPort string) ([]byte, uint16) {
arr := strings.Split(ipPort, ipPortSeparator)
if len(arr) == 2 {
port, err := strconv.ParseUint(arr[1], 0, 16)
if err != nil {
log.Warningf("Error extracting port. %#v", err)
return []byte(ipPort), defaultPort
}
return []byte(arr[0]), uint16(port)
}
return []byte(ipPort), defaultPort
}
func combineIPAndPort(ip net.IP, port uint16) string {
if port == 0 || port == defaultPort {
return ip.String()
} else {
strPort := strconv.Itoa(int(port))
return ip.String() + ipPortSeparator + strPort
}
}

View File

@@ -0,0 +1,235 @@
// Copyright (c) 2016-2021 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"
"math/big"
"reflect"
"regexp"
"strings"
log "github.com/sirupsen/logrus"
"github.com/projectcalico/calico/libcalico-go/lib/errors"
"github.com/projectcalico/calico/libcalico-go/lib/net"
)
const (
// Common attributes which may be set on allocations by clients.
IPAMBlockAttributePod = "pod"
IPAMBlockAttributeNamespace = "namespace"
IPAMBlockAttributeNode = "node"
IPAMBlockAttributeType = "type"
IPAMBlockAttributeTypeIPIP = "ipipTunnelAddress"
IPAMBlockAttributeTypeVXLAN = "vxlanTunnelAddress"
IPAMBlockAttributeTypeVXLANV6 = "vxlanV6TunnelAddress"
IPAMBlockAttributeTypeWireguard = "wireguardTunnelAddress"
IPAMBlockAttributeTypeWireguardV6 = "wireguardV6TunnelAddress"
IPAMBlockAttributeTimestamp = "timestamp"
)
var (
matchBlock = regexp.MustCompile("^/?calico/ipam/v2/assignment/ipv./block/([^/]+)$")
typeBlock = reflect.TypeOf(AllocationBlock{})
)
type BlockKey struct {
CIDR net.IPNet `json:"-" validate:"required,name"`
}
func (key BlockKey) defaultPath() (string, error) {
if key.CIDR.IP == nil {
return "", errors.ErrorInsufficientIdentifiers{}
}
c := strings.Replace(key.CIDR.String(), "/", "-", 1)
e := fmt.Sprintf("/calico/ipam/v2/assignment/ipv%d/block/%s", key.CIDR.Version(), c)
return e, nil
}
func (key BlockKey) defaultDeletePath() (string, error) {
return key.defaultPath()
}
func (key BlockKey) defaultDeleteParentPaths() ([]string, error) {
return nil, nil
}
func (key BlockKey) valueType() (reflect.Type, error) {
return typeBlock, nil
}
func (key BlockKey) String() string {
return fmt.Sprintf("BlockKey(cidr=%s)", key.CIDR.String())
}
type BlockListOptions struct {
IPVersion int `json:"-"`
}
func (options BlockListOptions) defaultPathRoot() string {
k := "/calico/ipam/v2/assignment/"
if options.IPVersion != 0 {
k = k + fmt.Sprintf("ipv%d/", options.IPVersion)
}
return k
}
func (options BlockListOptions) KeyFromDefaultPath(path string) Key {
log.Debugf("Get Block key from %s", path)
r := matchBlock.FindAllStringSubmatch(path, -1)
if len(r) != 1 {
log.Debugf("%s didn't match regex", path)
return nil
}
cidrStr := strings.Replace(r[0][1], "-", "/", 1)
_, cidr, err := net.ParseCIDR(cidrStr)
if err != nil {
log.Debugf("find an invalid cidr %s for path=%v , info=%v ", r[0][1], path, err)
return nil
}
return BlockKey{CIDR: *cidr}
}
type AllocationBlock struct {
// The block's CIDR.
CIDR net.IPNet `json:"cidr"`
// Affinity of the block, if this block has one. If set, it will be of the form
// "host:<hostname>". If not set, this block is not affine to a host.
Affinity *string `json:"affinity"`
// Array of allocations in-use within this block. nil entries mean the allocation is free.
// For non-nil entries at index i, the index is the ordinal of the allocation within this block
// and the value is the index of the associated attributes in the Attributes array.
Allocations []*int `json:"allocations"`
// Unallocated is an ordered list of allocations which are free in the block.
Unallocated []int `json:"unallocated"`
// Attributes is an array of arbitrary metadata associated with allocations in the block. To find
// attributes for a given allocation, use the value of the allocation's entry in the Allocations array
// as the index of the element in this array.
Attributes []AllocationAttribute `json:"attributes"`
// We store a sequence number that is updated each time the block is written.
// Each allocation will also store the sequence number of the block at the time of its creation.
// When releasing an IP, passing the sequence number associated with the allocation allows us
// to protect against a race condition and ensure the IP hasn't been released and re-allocated
// since the release request.
SequenceNumber uint64 `json:"sequenceNumber"`
// Map of allocated ordinal within the block to sequence number of the block at
// the time of allocation. Kubernetes does not allow numerical keys for maps, so
// the key is cast to a string.
SequenceNumberForAllocation map[string]uint64 `json:"sequenceNumberForAllocation"`
// Deleted is an internal boolean used to workaround a limitation in the Kubernetes API whereby
// deletion will not return a conflict error if the block has been updated.
Deleted bool `json:"deleted"`
// HostAffinity is deprecated in favor of Affinity.
// This is only to keep compatibility with existing deployments.
// The data format should be `Affinity: host:hostname` (not `hostAffinity: hostname`).
HostAffinity *string `json:"hostAffinity,omitempty"`
}
func (b *AllocationBlock) SetSequenceNumberForOrdinal(ordinal int) {
if b.SequenceNumberForAllocation == nil {
b.SequenceNumberForAllocation = map[string]uint64{}
}
b.SequenceNumberForAllocation[fmt.Sprintf("%d", ordinal)] = b.SequenceNumber
}
func (b *AllocationBlock) GetSequenceNumberForOrdinal(ordinal int) uint64 {
return b.SequenceNumberForAllocation[fmt.Sprintf("%d", ordinal)]
}
func (b *AllocationBlock) ClearSequenceNumberForOrdinal(ordinal int) {
delete(b.SequenceNumberForAllocation, fmt.Sprintf("%d", ordinal))
}
func (b *AllocationBlock) MarkDeleted() {
b.Deleted = true
}
func (b *AllocationBlock) IsDeleted() bool {
return b.Deleted
}
func (b *AllocationBlock) Host() string {
if b.Affinity != nil && strings.HasPrefix(*b.Affinity, "host:") {
return strings.TrimPrefix(*b.Affinity, "host:")
}
return ""
}
type Allocation struct {
Addr net.IP
Host string
}
func (b *AllocationBlock) NonAffineAllocations() []Allocation {
var allocs []Allocation
myHost := b.Host()
for ordinal, attrIdx := range b.Allocations {
if attrIdx == nil {
continue // Skip unallocated IPs.
}
if *attrIdx >= len(b.Attributes) {
log.WithField("block", b).Warnf("Missing attributes for IP with ordinal %d", ordinal)
continue
}
attrs := b.Attributes[*attrIdx]
host := attrs.AttrSecondary[IPAMBlockAttributeNode]
if myHost != "" && host == myHost {
continue // Skip allocations that are affine to this block.
}
a := Allocation{
Addr: b.OrdinalToIP(ordinal),
Host: host,
}
allocs = append(allocs, a)
}
return allocs
}
// Get number of addresses covered by the block
func (b *AllocationBlock) NumAddresses() int {
ones, size := b.CIDR.Mask.Size()
numAddresses := 1 << uint(size-ones)
return numAddresses
}
// Find the ordinal (i.e. how far into the block) a given IP lies. Returns an error if the IP is outside the block.
func (b *AllocationBlock) IPToOrdinal(ip net.IP) (int, error) {
ipAsInt := net.IPToBigInt(ip)
baseInt := net.IPToBigInt(net.IP{b.CIDR.IP})
ord := big.NewInt(0).Sub(ipAsInt, baseInt).Int64()
if ord < 0 || ord >= int64(b.NumAddresses()) {
return 0, fmt.Errorf("IP %s not in block %s", ip, b.CIDR)
}
return int(ord), nil
}
// Calculates the IP at the given position within the block. ord=0 gives the first IP in the block.
func (b *AllocationBlock) OrdinalToIP(ord int) net.IP {
return b.CIDR.NthIP(ord)
}
type AllocationAttribute struct {
AttrPrimary *string `json:"handle_id"`
AttrSecondary map[string]string `json:"secondary"`
}

View File

@@ -0,0 +1,118 @@
// Copyright (c) 2016-2019 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"
"reflect"
"regexp"
"strings"
log "github.com/sirupsen/logrus"
"github.com/projectcalico/calico/libcalico-go/lib/errors"
"github.com/projectcalico/calico/libcalico-go/lib/net"
)
var (
matchBlockAffinity = regexp.MustCompile("^/?calico/ipam/v2/host/([^/]+)/ipv./block/([^/]+)$")
typeBlockAff = reflect.TypeOf(BlockAffinity{})
)
type BlockAffinityState string
const (
StateConfirmed BlockAffinityState = "confirmed"
StatePending BlockAffinityState = "pending"
StatePendingDeletion BlockAffinityState = "pendingDeletion"
)
type BlockAffinityKey struct {
CIDR net.IPNet `json:"-" validate:"required,name"`
Host string `json:"-"`
}
type BlockAffinity struct {
State BlockAffinityState `json:"state"`
Deleted bool `json:"deleted"`
}
func (key BlockAffinityKey) defaultPath() (string, error) {
if key.CIDR.IP == nil || key.Host == "" {
return "", errors.ErrorInsufficientIdentifiers{}
}
c := strings.Replace(key.CIDR.String(), "/", "-", 1)
e := fmt.Sprintf("/calico/ipam/v2/host/%s/ipv%d/block/%s", key.Host, key.CIDR.Version(), c)
return e, nil
}
func (key BlockAffinityKey) defaultDeletePath() (string, error) {
return key.defaultPath()
}
func (key BlockAffinityKey) defaultDeleteParentPaths() ([]string, error) {
return nil, nil
}
func (key BlockAffinityKey) valueType() (reflect.Type, error) {
return typeBlockAff, nil
}
func (key BlockAffinityKey) String() string {
return fmt.Sprintf("BlockAffinityKey(cidr=%s, host=%s)", key.CIDR, key.Host)
}
type BlockAffinityListOptions struct {
Host string
IPVersion int
}
func (options BlockAffinityListOptions) defaultPathRoot() string {
k := "/calico/ipam/v2/host/"
if options.Host != "" {
k = k + options.Host
if options.IPVersion != 0 {
k = k + fmt.Sprintf("/ipv%d/block", options.IPVersion)
}
}
return k
}
func (options BlockAffinityListOptions) KeyFromDefaultPath(path string) Key {
log.Debugf("Get Block affinity key from %s", path)
r := matchBlockAffinity.FindAllStringSubmatch(path, -1)
if len(r) != 1 {
log.Debugf("%s didn't match regex", path)
return nil
}
cidrStr := strings.Replace(r[0][2], "-", "/", 1)
_, cidr, _ := net.ParseCIDR(cidrStr)
if cidr == nil {
log.Debugf("Failed to parse CIDR in block affinity path: %q", path)
return nil
}
host := r[0][1]
if options.Host != "" && options.Host != host {
log.Debugf("Didn't match hostname: %s != %s", options.Host, host)
return nil
}
if options.IPVersion != 0 && options.IPVersion != cidr.Version() {
log.Debugf("Didn't match IP version. %d != %d", options.IPVersion, cidr.Version())
return nil
}
return BlockAffinityKey{CIDR: *cidr, Host: host}
}

View File

@@ -0,0 +1,20 @@
// Copyright (c) 2019 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
type DeletionMarker interface {
MarkDeleted()
IsDeleted() bool
}

View File

@@ -0,0 +1,183 @@
// Copyright (c) 2016-2018 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"
"reflect"
"regexp"
log "github.com/sirupsen/logrus"
"github.com/projectcalico/calico/libcalico-go/lib/errors"
)
var (
matchGlobalConfig = regexp.MustCompile("^/?calico/v1/config/(.+)$")
matchHostConfig = regexp.MustCompile("^/?calico/v1/host/([^/]+)/config/(.+)$")
matchReadyFlag = regexp.MustCompile("^/calico/v1/Ready$")
typeGlobalConfig = rawStringType
typeHostConfig = rawStringType
typeReadyFlag = rawBoolType
)
type ReadyFlagKey struct {
}
func (key ReadyFlagKey) defaultPath() (string, error) {
return "/calico/v1/Ready", nil
}
func (key ReadyFlagKey) defaultDeletePath() (string, error) {
return key.defaultPath()
}
func (key ReadyFlagKey) defaultDeleteParentPaths() ([]string, error) {
return nil, nil
}
func (key ReadyFlagKey) valueType() (reflect.Type, error) {
return typeReadyFlag, nil
}
func (key ReadyFlagKey) String() string {
return "ReadyFlagKey()"
}
type GlobalConfigKey struct {
Name string `json:"-" validate:"required,name"`
}
func (key GlobalConfigKey) defaultPath() (string, error) {
if key.Name == "" {
return "", errors.ErrorInsufficientIdentifiers{Name: "name"}
}
e := fmt.Sprintf("/calico/v1/config/%s", key.Name)
return e, nil
}
func (key GlobalConfigKey) defaultDeletePath() (string, error) {
return key.defaultPath()
}
func (key GlobalConfigKey) defaultDeleteParentPaths() ([]string, error) {
return nil, nil
}
func (key GlobalConfigKey) valueType() (reflect.Type, error) {
return typeGlobalConfig, nil
}
func (key GlobalConfigKey) String() string {
return fmt.Sprintf("GlobalFelixConfig(name=%s)", key.Name)
}
type GlobalConfigListOptions struct {
Name string
}
func (options GlobalConfigListOptions) defaultPathRoot() string {
k := "/calico/v1/config"
if options.Name == "" {
return k
}
k = k + fmt.Sprintf("/%s", options.Name)
return k
}
func (options GlobalConfigListOptions) KeyFromDefaultPath(path string) Key {
log.Debugf("Get GlobalConfig key from %s", path)
r := matchGlobalConfig.FindAllStringSubmatch(path, -1)
if len(r) != 1 {
log.Debugf("Didn't match regex")
return nil
}
name := r[0][1]
if options.Name != "" && name != options.Name {
log.Debugf("Didn't match name %s != %s", options.Name, name)
return nil
}
return GlobalConfigKey{Name: name}
}
type HostConfigKey struct {
Hostname string `json:"-" validate:"required,name"`
Name string `json:"-" validate:"required,name"`
}
func (key HostConfigKey) defaultPath() (string, error) {
if key.Name == "" {
return "", errors.ErrorInsufficientIdentifiers{Name: "name"}
}
if key.Hostname == "" {
return "", errors.ErrorInsufficientIdentifiers{Name: "node"}
}
e := fmt.Sprintf("/calico/v1/host/%s/config/%s", key.Hostname, key.Name)
return e, nil
}
func (key HostConfigKey) defaultDeletePath() (string, error) {
return key.defaultPath()
}
func (key HostConfigKey) defaultDeleteParentPaths() ([]string, error) {
return nil, nil
}
func (key HostConfigKey) valueType() (reflect.Type, error) {
return typeHostConfig, nil
}
func (key HostConfigKey) String() string {
return fmt.Sprintf("HostConfig(node=%s,name=%s)", key.Hostname, key.Name)
}
type HostConfigListOptions struct {
Hostname string
Name string
}
func (options HostConfigListOptions) defaultPathRoot() string {
k := "/calico/v1/host"
if options.Hostname == "" {
return k
}
k = k + fmt.Sprintf("/%s/config", options.Hostname)
if options.Name == "" {
return k
}
k = k + fmt.Sprintf("/%s", options.Name)
return k
}
func (options HostConfigListOptions) KeyFromDefaultPath(path string) Key {
log.Debugf("Get HostConfig key from %s", path)
r := matchHostConfig.FindAllStringSubmatch(path, -1)
if len(r) != 1 {
log.Debugf("Didn't match regex")
return nil
}
hostname := r[0][1]
name := r[0][2]
if options.Hostname != "" && hostname != options.Hostname {
log.Debugf("Didn't match hostname %s != %s", options.Hostname, hostname)
return nil
}
if options.Name != "" && name != options.Name {
log.Debugf("Didn't match name %s != %s", options.Name, name)
return nil
}
return HostConfigKey{Hostname: hostname, Name: name}
}

View File

@@ -0,0 +1,113 @@
// Copyright (c) 2016-2017 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"
log "github.com/sirupsen/logrus"
"github.com/projectcalico/calico/libcalico-go/lib/errors"
"github.com/projectcalico/calico/libcalico-go/lib/net"
)
var (
matchHostEndpoint = regexp.MustCompile("^/?calico/v1/host/([^/]+)/endpoint/([^/]+)$")
typeHostEndpoint = reflect.TypeOf(HostEndpoint{})
)
type HostEndpointKey struct {
Hostname string `json:"-" validate:"required,hostname"`
EndpointID string `json:"-" validate:"required,namespacedName"`
}
func (key HostEndpointKey) defaultPath() (string, error) {
if key.Hostname == "" {
return "", errors.ErrorInsufficientIdentifiers{Name: "node"}
}
if key.EndpointID == "" {
return "", errors.ErrorInsufficientIdentifiers{Name: "name"}
}
e := fmt.Sprintf("/calico/v1/host/%s/endpoint/%s",
key.Hostname, escapeName(key.EndpointID))
return e, nil
}
func (key HostEndpointKey) defaultDeletePath() (string, error) {
return key.defaultPath()
}
func (key HostEndpointKey) defaultDeleteParentPaths() ([]string, error) {
return nil, nil
}
func (key HostEndpointKey) valueType() (reflect.Type, error) {
return typeHostEndpoint, nil
}
func (key HostEndpointKey) String() string {
return fmt.Sprintf("HostEndpoint(node=%s, name=%s)", key.Hostname, key.EndpointID)
}
type HostEndpointListOptions struct {
Hostname string
EndpointID string
}
func (options HostEndpointListOptions) defaultPathRoot() string {
k := "/calico/v1/host"
if options.Hostname == "" {
return k
}
k = k + fmt.Sprintf("/%s/endpoint", options.Hostname)
if options.EndpointID == "" {
return k
}
k = k + fmt.Sprintf("/%s", escapeName(options.EndpointID))
return k
}
func (options HostEndpointListOptions) KeyFromDefaultPath(path string) Key {
log.Debugf("Get HostEndpoint key from %s", path)
r := matchHostEndpoint.FindAllStringSubmatch(path, -1)
if len(r) != 1 {
log.Debugf("Didn't match regex")
return nil
}
hostname := r[0][1]
endpointID := unescapeName(r[0][2])
if options.Hostname != "" && hostname != options.Hostname {
log.Debugf("Didn't match hostname %s != %s", options.Hostname, hostname)
return nil
}
if options.EndpointID != "" && endpointID != options.EndpointID {
log.Debugf("Didn't match endpointID %s != %s", options.EndpointID, endpointID)
return nil
}
return HostEndpointKey{Hostname: hostname, EndpointID: endpointID}
}
type HostEndpoint struct {
Name string `json:"name,omitempty" validate:"omitempty,interface"`
ExpectedIPv4Addrs []net.IP `json:"expected_ipv4_addrs,omitempty" validate:"omitempty,dive,ipv4"`
ExpectedIPv6Addrs []net.IP `json:"expected_ipv6_addrs,omitempty" validate:"omitempty,dive,ipv6"`
Labels map[string]string `json:"labels,omitempty" validate:"omitempty,labels"`
ProfileIDs []string `json:"profile_ids,omitempty" validate:"omitempty,dive,name"`
Ports []EndpointPort `json:"ports,omitempty" validate:"dive"`
}

View File

@@ -0,0 +1,107 @@
// 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"
log "github.com/sirupsen/logrus"
"github.com/projectcalico/calico/libcalico-go/lib/errors"
)
var (
matchHostEndpointStatus = regexp.MustCompile("^/?calico/felix/v1/host/([^/]+)/endpoint/([^/]+)$")
typeHostEndpointStatus = reflect.TypeOf(HostEndpointStatus{})
)
type HostEndpointStatusKey struct {
Hostname string `json:"-" validate:"required,hostname"`
EndpointID string `json:"-" validate:"required,namespacedName"`
}
func (key HostEndpointStatusKey) defaultPath() (string, error) {
if key.Hostname == "" {
return "", errors.ErrorInsufficientIdentifiers{Name: "node"}
}
if key.EndpointID == "" {
return "", errors.ErrorInsufficientIdentifiers{Name: "name"}
}
e := fmt.Sprintf("/calico/felix/v1/host/%s/endpoint/%s",
key.Hostname, escapeName(key.EndpointID))
return e, nil
}
func (key HostEndpointStatusKey) defaultDeletePath() (string, error) {
return key.defaultPath()
}
func (key HostEndpointStatusKey) defaultDeleteParentPaths() ([]string, error) {
return nil, nil
}
func (key HostEndpointStatusKey) valueType() (reflect.Type, error) {
return typeHostEndpointStatus, nil
}
func (key HostEndpointStatusKey) String() string {
return fmt.Sprintf("HostEndpointStatus(hostname=%s, name=%s)", key.Hostname, key.EndpointID)
}
type HostEndpointStatusListOptions struct {
Hostname string
EndpointID string
}
func (options HostEndpointStatusListOptions) defaultPathRoot() string {
k := "/calico/felix/v1/host"
if options.Hostname == "" {
return k
}
k = k + fmt.Sprintf("/%s/endpoint", options.Hostname)
if options.EndpointID == "" {
return k
}
k = k + fmt.Sprintf("/%s", escapeName(options.EndpointID))
return k
}
func (options HostEndpointStatusListOptions) KeyFromDefaultPath(ekey string) Key {
log.Debugf("Get HostEndpointStatus key from %s", ekey)
r := matchHostEndpointStatus.FindAllStringSubmatch(ekey, -1)
if len(r) != 1 {
log.Debugf("Didn't match regex")
return nil
}
hostname := r[0][1]
endpointID := unescapeName(r[0][2])
if options.Hostname != "" && hostname != options.Hostname {
log.Debugf("Didn't match hostname %s != %s", options.Hostname, hostname)
return nil
}
if options.EndpointID != "" && endpointID != options.EndpointID {
log.Debugf("Didn't match endpointID %s != %s", options.EndpointID, endpointID)
return nil
}
return HostEndpointStatusKey{Hostname: hostname, EndpointID: endpointID}
}
type HostEndpointStatus struct {
Status string `json:"status"`
}

View File

@@ -0,0 +1,53 @@
// 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 (
"reflect"
)
const (
IPAMConfigGlobalName = "default"
)
var typeIPAMConfig = reflect.TypeOf(IPAMConfig{})
type IPAMConfigKey struct{}
func (key IPAMConfigKey) defaultPath() (string, error) {
return "/calico/ipam/v2/config", nil
}
func (key IPAMConfigKey) defaultDeletePath() (string, error) {
return key.defaultPath()
}
func (key IPAMConfigKey) defaultDeleteParentPaths() ([]string, error) {
return nil, nil
}
func (key IPAMConfigKey) valueType() (reflect.Type, error) {
return typeIPAMConfig, nil
}
func (key IPAMConfigKey) String() string {
return "IPAMConfigKey()"
}
type IPAMConfig struct {
StrictAffinity bool `json:"strict_affinity,omitempty"`
AutoAllocateBlocks bool `json:"auto_allocate_blocks,omitempty"`
MaxBlocksPerHost int `json:"maxBlocksPerHost,omitempty"`
}

View File

@@ -0,0 +1,84 @@
// Copyright (c) 2016,2020 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"
"reflect"
"regexp"
log "github.com/sirupsen/logrus"
"github.com/projectcalico/calico/libcalico-go/lib/errors"
)
var (
matchHandle = regexp.MustCompile("^/?calico/ipam/v2/handle/([^/]+)$")
typeHandle = reflect.TypeOf(IPAMHandle{})
)
type IPAMHandleKey struct {
HandleID string `json:"id"`
}
func (key IPAMHandleKey) defaultPath() (string, error) {
if key.HandleID == "" {
return "", errors.ErrorInsufficientIdentifiers{}
}
e := fmt.Sprintf("/calico/ipam/v2/handle/%s", key.HandleID)
return e, nil
}
func (key IPAMHandleKey) defaultDeletePath() (string, error) {
return key.defaultPath()
}
func (key IPAMHandleKey) defaultDeleteParentPaths() ([]string, error) {
return nil, nil
}
func (key IPAMHandleKey) valueType() (reflect.Type, error) {
return typeHandle, nil
}
func (key IPAMHandleKey) String() string {
return fmt.Sprintf("IPAMHandleKey(id=%s)", key.HandleID)
}
type IPAMHandleListOptions struct {
// TODO: Have some options here?
}
func (options IPAMHandleListOptions) defaultPathRoot() string {
k := "/calico/ipam/v2/handle/"
// TODO: Allow filtering on individual host?
return k
}
func (options IPAMHandleListOptions) KeyFromDefaultPath(path string) Key {
log.Debugf("Get IPAM handle key from %s", path)
r := matchHandle.FindAllStringSubmatch(path, -1)
if len(r) != 1 {
log.Debugf("%s didn't match regex", path)
return nil
}
return IPAMHandleKey{HandleID: r[0][1]}
}
type IPAMHandle struct {
HandleID string `json:"-"`
Block map[string]int `json:"block"`
Deleted bool `json:"deleted"`
}

View File

@@ -0,0 +1,58 @@
// 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"
"reflect"
"github.com/projectcalico/calico/libcalico-go/lib/errors"
)
var (
typeIPAMHost = reflect.TypeOf(IPAMHost{})
)
type IPAMHostKey struct {
Host string
}
func (key IPAMHostKey) defaultPath() (string, error) {
if key.Host == "" {
return "", errors.ErrorInsufficientIdentifiers{Name: "host"}
}
k := "/calico/ipam/v2/host/" + key.Host
return k, nil
}
func (key IPAMHostKey) defaultDeletePath() (string, error) {
return key.defaultPath()
}
func (key IPAMHostKey) defaultDeleteParentPaths() ([]string, error) {
return nil, nil
}
func (key IPAMHostKey) valueType() (reflect.Type, error) {
return typeIPAMHost, nil
}
func (key IPAMHostKey) String() string {
return fmt.Sprintf("IPAMHostKey(host=%s)", key.Host)
}
type IPAMHost struct {
}

View File

@@ -0,0 +1,107 @@
// Copyright (c) 2016,2021 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"
"reflect"
"regexp"
"strings"
log "github.com/sirupsen/logrus"
"github.com/projectcalico/calico/libcalico-go/lib/backend/encap"
"github.com/projectcalico/calico/libcalico-go/lib/errors"
"github.com/projectcalico/calico/libcalico-go/lib/net"
)
var (
matchIPPool = regexp.MustCompile("^/?calico/v1/ipam/v./pool/([^/]+)$")
typeIPPool = reflect.TypeOf(IPPool{})
)
type IPPoolKey struct {
CIDR net.IPNet `json:"-" validate:"required,name"`
}
func (key IPPoolKey) defaultPath() (string, error) {
if key.CIDR.IP == nil {
return "", errors.ErrorInsufficientIdentifiers{Name: "cidr"}
}
c := strings.Replace(key.CIDR.String(), "/", "-", 1)
e := fmt.Sprintf("/calico/v1/ipam/v%d/pool/%s", key.CIDR.Version(), c)
return e, nil
}
func (key IPPoolKey) defaultDeletePath() (string, error) {
return key.defaultPath()
}
func (key IPPoolKey) defaultDeleteParentPaths() ([]string, error) {
return nil, nil
}
func (key IPPoolKey) valueType() (reflect.Type, error) {
return typeIPPool, nil
}
func (key IPPoolKey) String() string {
return fmt.Sprintf("IPPool(cidr=%s)", key.CIDR)
}
type IPPoolListOptions struct {
CIDR net.IPNet
}
func (options IPPoolListOptions) defaultPathRoot() string {
k := "/calico/v1/ipam/"
if options.CIDR.IP == nil {
return k
}
c := strings.Replace(options.CIDR.String(), "/", "-", 1)
k = k + fmt.Sprintf("v%d/pool/", options.CIDR.Version()) + fmt.Sprintf("%s", c)
return k
}
func (options IPPoolListOptions) KeyFromDefaultPath(path string) Key {
log.Debugf("Get Pool key from %s", path)
r := matchIPPool.FindAllStringSubmatch(path, -1)
if len(r) != 1 {
log.Debugf("%s didn't match regex", path)
return nil
}
cidrStr := strings.Replace(r[0][1], "-", "/", 1)
_, cidr, err := net.ParseCIDR(cidrStr)
if err != nil {
log.WithError(err).Warningf("Failed to parse CIDR %s", cidrStr)
return nil
}
if options.CIDR.IP != nil && !reflect.DeepEqual(*cidr, options.CIDR) {
log.Debugf("Didn't match cidr %s != %s", options.CIDR.String(), cidr.String())
return nil
}
return IPPoolKey{CIDR: *cidr}
}
type IPPool struct {
CIDR net.IPNet `json:"cidr"`
IPIPInterface string `json:"ipip"`
IPIPMode encap.Mode `json:"ipip_mode"`
VXLANMode encap.Mode `json:"vxlan_mode"`
Masquerade bool `json:"masquerade"`
IPAM bool `json:"ipam"`
Disabled bool `json:"disabled"`
DisableBGPExport bool `json:"disableBGPExport"`
}

View File

@@ -0,0 +1,627 @@
// Copyright (c) 2016-2020 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 (
"bytes"
"fmt"
net2 "net"
"reflect"
"strings"
"time"
log "github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/types"
"github.com/projectcalico/calico/libcalico-go/lib/json"
"github.com/projectcalico/calico/libcalico-go/lib/namespace"
"github.com/projectcalico/calico/libcalico-go/lib/net"
)
// RawString is used a value type to indicate that the value is a bare non-JSON string
type rawString string
type rawBool bool
type rawIP net.IP
var rawStringType = reflect.TypeOf(rawString(""))
var rawBoolType = reflect.TypeOf(rawBool(true))
var rawIPType = reflect.TypeOf(rawIP{})
// Key represents a parsed datastore key.
type Key interface {
// defaultPath() returns a common path representation of the object used by
// etcdv3 and other datastores.
defaultPath() (string, error)
// defaultDeletePath() returns a common path representation used by etcdv3
// and other datastores to delete the object.
defaultDeletePath() (string, error)
// defaultDeleteParentPaths() returns an ordered slice of paths that should
// be removed after deleting the primary path (given by defaultDeletePath),
// provided there are no child entries associated with those paths. This is
// only used by directory based KV stores (such as etcdv3). With a directory
// based KV store, creation of a resource may also create parent directory entries
// that could be shared by multiple resources, and therefore the parent directories
// can only be removed when there are no more resources under them. The list of
// parent paths is ordered, and directories should be removed in the order supplied
// in the slice and only if the directory is empty.
defaultDeleteParentPaths() ([]string, error)
// valueType returns the object type associated with this key.
valueType() (reflect.Type, error)
// String returns a unique string representation of this key. The string
// returned by this method must uniquely identify this Key.
String() string
}
// Interface used to perform datastore lookups.
type ListInterface interface {
// defaultPathRoot() returns a default stringified root path, i.e. path
// to the directory containing all the keys to be listed.
defaultPathRoot() string
// BUG(smc) I think we should remove this and use the package KeyFromDefaultPath function.
// KeyFromDefaultPath parses the default path representation of the
// Key type for this list. It returns nil if passed a different kind
// of path.
KeyFromDefaultPath(key string) Key
}
// KVPair holds a typed key and value object as well as datastore specific
// revision information.
//
// The Value is dependent on the Key, but in general will be on of the following
// types:
// - A pointer to a struct
// - A slice or map
// - A bare string, boolean value or IP address (i.e. without quotes, so not
// JSON format).
type KVPair struct {
Key Key
Value interface{}
Revision string
UID *types.UID
TTL time.Duration // For writes, if non-zero, key has a TTL.
}
// KVPairList hosts a slice of KVPair structs and a Revision, returned from a Ls
type KVPairList struct {
KVPairs []*KVPair
Revision string
}
// KeyToDefaultPath converts one of the Keys from this package into a unique
// '/'-delimited path, which is suitable for use as the key when storing the
// value in a hierarchical (i.e. one with directories and leaves) key/value
// datastore such as etcd v3.
//
// Each unique key returns a unique path.
//
// Keys with a hierarchical relationship share a common prefix. However, in
// order to support datastores that do not support storing data at non-leaf
// nodes in the hierarchy (such as etcd v3), the path returned for a "parent"
// key, is not a direct ancestor of its children.
func KeyToDefaultPath(key Key) (string, error) {
return key.defaultPath()
}
// KeyToDefaultDeletePath converts one of the Keys from this package into a
// unique '/'-delimited path, which is suitable for use as the key when
// (recursively) deleting the value from a hierarchical (i.e. one with
// directories and leaves) key/value datastore such as etcd v3.
//
// KeyToDefaultDeletePath returns a different path to KeyToDefaultPath when
// it is a passed a Key that represents a non-leaf which, for example, has its
// own metadata but also contains other resource types as children.
//
// KeyToDefaultDeletePath returns the common prefix of the non-leaf key and
// its children so that a recursive delete of that key would delete the
// object itself and any children it has.
func KeyToDefaultDeletePath(key Key) (string, error) {
return key.defaultDeletePath()
}
// KeyToDefaultDeleteParentPaths returns a slice of '/'-delimited
// paths which are used to delete parent entries that may be auto-created
// by directory-based KV stores (e.g. etcd v3). These paths should also be
// removed provided they have no more child entries.
//
// The list of parent paths is ordered, and directories should be removed
// in the order supplied in the slice and only if the directory is empty.
//
// For example,
//
// KeyToDefaultDeletePaths(WorkloadEndpointKey{
// Nodename: "h",
// OrchestratorID: "o",
// WorkloadID: "w",
// EndpointID: "e",
// })
//
// returns
//
// ["/calico/v1/host/h/workload/o/w/endpoint",
//
// "/calico/v1/host/h/workload/o/w"]
//
// indicating that these paths should also be deleted when they are empty.
// In this example it is equivalent to deleting the workload when there are
// no more endpoints in the workload.
func KeyToDefaultDeleteParentPaths(key Key) ([]string, error) {
return key.defaultDeleteParentPaths()
}
// ListOptionsToDefaultPathRoot converts list options struct into a
// common-prefix path suitable for querying a datastore that uses the paths
// returned by KeyToDefaultPath.
func ListOptionsToDefaultPathRoot(listOptions ListInterface) string {
return listOptions.defaultPathRoot()
}
// ListOptionsIsFullyQualified returns true if the options actually specify a fully
// qualified resource rather than a partial match.
func ListOptionsIsFullyQualified(listOptions ListInterface) bool {
// Construct the path prefix and then check to see if that actually corresponds to
// the path of a resource instance.
return listOptions.KeyFromDefaultPath(listOptions.defaultPathRoot()) != nil
}
// IsListOptionsLastSegmentPrefix returns true if the final segment of the default path
// root is a name prefix rather than the full name.
func IsListOptionsLastSegmentPrefix(listOptions ListInterface) bool {
// Only supported for ResourceListOptions.
rl, ok := listOptions.(ResourceListOptions)
return ok && rl.IsLastSegmentIsPrefix()
}
// KeyFromDefaultPath parses the default path representation of a key into one
// of our <Type>Key structs. Returns nil if the string doesn't match one of
// our key types.
func KeyFromDefaultPath(path string) Key {
// "v3" resource keys strictly require a leading slash but older "v1" keys were permissive.
// For ease of parsing, strip the slash off now but pass it down to keyFromDefaultPathInner so
// it can check for it later.
normalizedPath := path
if strings.HasPrefix(normalizedPath, "/") {
normalizedPath = normalizedPath[1:]
}
parts := strings.Split(normalizedPath, "/")
if len(parts) < 3 {
// After removing the optional `/` prefix, should have at least 3 segments.
return nil
}
return keyFromDefaultPathInner(path, parts)
}
func keyFromDefaultPathInner(path string, parts []string) Key {
if parts[0] != "calico" {
return nil
}
switch parts[1] {
case "v1":
switch parts[2] {
case "ipam":
return IPPoolListOptions{}.KeyFromDefaultPath(path)
case "config":
return GlobalConfigKey{Name: strings.Join(parts[3:], "/")}
case "host":
if len(parts) < 5 {
return nil
}
hostname := parts[3]
switch parts[4] {
case "workload":
if len(parts) != 9 || parts[7] != "endpoint" {
return nil
}
return WorkloadEndpointKey{
Hostname: unescapeName(hostname),
OrchestratorID: unescapeName(parts[5]),
WorkloadID: unescapeName(parts[6]),
EndpointID: unescapeName(parts[8]),
}
case "endpoint":
if len(parts) != 6 {
return nil
}
return HostEndpointKey{
Hostname: unescapeName(hostname),
EndpointID: unescapeName(parts[5]),
}
case "config":
return HostConfigKey{
Hostname: hostname,
Name: strings.Join(parts[5:], "/"),
}
case "metadata":
if len(parts) != 5 {
return nil
}
return HostMetadataKey{
Hostname: hostname,
}
case "bird_ip":
if len(parts) != 5 {
return nil
}
return HostIPKey{
Hostname: hostname,
}
case "wireguard":
if len(parts) != 5 {
return nil
}
return WireguardKey{
NodeName: hostname,
}
}
case "netset":
if len(parts) != 4 {
return nil
}
return NetworkSetKey{
Name: unescapeName(parts[3]),
}
case "Ready":
if len(parts) > 3 || path[0] != '/' {
return nil
}
return ReadyFlagKey{}
case "policy":
if len(parts) < 6 {
return nil
}
switch parts[3] {
case "tier":
if len(parts) < 6 {
return nil
}
switch parts[5] {
case "policy":
if len(parts) != 7 {
return nil
}
return PolicyKey{
Name: unescapeName(parts[6]),
}
}
case "profile":
pk := unescapeName(parts[4])
switch parts[5] {
case "rules":
return ProfileRulesKey{ProfileKey: ProfileKey{pk}}
case "labels":
return ProfileLabelsKey{ProfileKey: ProfileKey{pk}}
}
}
}
case "bgp":
switch parts[2] {
case "v1":
if len(parts) < 5 {
return nil
}
switch parts[3] {
case "global":
return GlobalBGPConfigListOptions{}.KeyFromDefaultPath(path)
case "host":
if len(parts) < 6 {
return nil
}
return NodeBGPConfigListOptions{}.KeyFromDefaultPath(path)
}
}
case "ipam":
if len(parts) < 5 {
return nil
}
switch parts[2] {
case "v2":
switch parts[3] {
case "assignment":
return BlockListOptions{}.KeyFromDefaultPath(path)
case "handle":
if len(parts) > 5 {
return nil
}
return IPAMHandleKey{
HandleID: parts[4],
}
case "host":
return BlockAffinityListOptions{}.KeyFromDefaultPath(path)
}
}
case "resources":
switch parts[2] {
case "v3":
// v3 resource keys strictly require the leading slash.
if len(parts) < 6 || parts[3] != "projectcalico.org" || path[0] != '/' {
return nil
}
switch len(parts) {
case 6:
ri, ok := resourceInfoByPlural[unescapeName(parts[4])]
if !ok {
log.Warnf("(BUG) unknown resource type: %v", path)
return nil
}
if namespace.IsNamespaced(ri.kind) {
log.Warnf("(BUG) Path is a global resource, but resource is namespaced: %v", path)
return nil
}
log.Debugf("Path is a global resource: %v", path)
return ResourceKey{
Kind: ri.kind,
Name: unescapeName(parts[5]),
}
case 7:
ri, ok := resourceInfoByPlural[unescapeName(parts[4])]
if !ok {
log.Warnf("(BUG) unknown resource type: %v", path)
return nil
}
if !namespace.IsNamespaced(ri.kind) {
log.Warnf("(BUG) Path is a namespaced resource, but resource is global: %v", path)
return nil
}
log.Debugf("Path is a namespaced resource: %v", path)
return ResourceKey{
Kind: ri.kind,
Namespace: unescapeName(parts[5]),
Name: unescapeName(parts[6]),
}
}
}
case "felix":
if len(parts) < 4 {
return nil
}
switch parts[2] {
case "v1":
switch parts[3] {
case "host":
if len(parts) != 7 || parts[5] != "endpoint" {
return nil
}
return HostEndpointStatusKey{
Hostname: parts[4],
EndpointID: unescapeName(parts[6]),
}
}
case "v2":
if len(parts) < 7 {
return nil
}
if parts[4] != "host" {
return nil
}
switch parts[6] {
case "status":
return ActiveStatusReportListOptions{}.KeyFromDefaultPath(path)
case "last_reported_status":
return LastStatusReportListOptions{}.KeyFromDefaultPath(path)
case "workload":
return WorkloadEndpointStatusListOptions{}.KeyFromDefaultPath(path)
}
}
}
log.Debugf("Path is unknown: %v", path)
return nil
}
// OldKeyFromDefaultPath is the old, (slower) implementation of KeyFromDefaultPath. It is kept to allow
// fuzzing the new version against it. Parses the default path representation of a key into one
// of our <Type>Key structs. Returns nil if the string doesn't match one of
// our key types.
func OldKeyFromDefaultPath(path string) Key {
if m := matchWorkloadEndpoint.FindStringSubmatch(path); m != nil {
log.Debugf("Path is a workload endpoint: %v", path)
return WorkloadEndpointKey{
Hostname: unescapeName(m[1]),
OrchestratorID: unescapeName(m[2]),
WorkloadID: unescapeName(m[3]),
EndpointID: unescapeName(m[4]),
}
} else if m := matchHostEndpoint.FindStringSubmatch(path); m != nil {
log.Debugf("Path is a host endpoint: %v", path)
return HostEndpointKey{
Hostname: unescapeName(m[1]),
EndpointID: unescapeName(m[2]),
}
} else if m := matchNetworkSet.FindStringSubmatch(path); m != nil {
log.Debugf("Path is a network set: %v", path)
return NetworkSetKey{
Name: unescapeName(m[1]),
}
} else if m := matchGlobalResource.FindStringSubmatch(path); m != nil {
ri, ok := resourceInfoByPlural[unescapeName(m[1])]
if !ok {
log.Warnf("(BUG) unknown resource type: %v", path)
return nil
}
if namespace.IsNamespaced(ri.kind) {
log.Warnf("(BUG) Path is a global resource, but resource is namespaced: %v", path)
return nil
}
log.Debugf("Path is a global resource: %v", path)
return ResourceKey{
Kind: ri.kind,
Name: unescapeName(m[2]),
}
} else if m := matchNamespacedResource.FindStringSubmatch(path); m != nil {
ri, ok := resourceInfoByPlural[unescapeName(m[1])]
if !ok {
log.Warnf("(BUG) unknown resource type: %v", path)
return nil
}
if !namespace.IsNamespaced(ri.kind) {
log.Warnf("(BUG) Path is a namespaced resource, but resource is global: %v", path)
return nil
}
log.Debugf("Path is a namespaced resource: %v", path)
return ResourceKey{
Kind: resourceInfoByPlural[unescapeName(m[1])].kind,
Namespace: unescapeName(m[2]),
Name: unescapeName(m[3]),
}
} else if m := matchPolicy.FindStringSubmatch(path); m != nil {
log.Debugf("Path is a policy: %v", path)
return PolicyKey{
Name: unescapeName(m[2]),
}
} else if m := matchProfile.FindStringSubmatch(path); m != nil {
log.Debugf("Path is a profile: %v (%v)", path, m[2])
pk := ProfileKey{unescapeName(m[1])}
switch m[2] {
case "rules":
log.Debugf("Profile rules")
return ProfileRulesKey{ProfileKey: pk}
case "labels":
log.Debugf("Profile labels")
return ProfileLabelsKey{ProfileKey: pk}
}
return nil
} else if m := matchHostIp.FindStringSubmatch(path); m != nil {
log.Debugf("Path is a host ID: %v", path)
return HostIPKey{Hostname: m[1]}
} else if m := matchWireguard.FindStringSubmatch(path); m != nil {
log.Debugf("Path is a node name: %v", path)
return WireguardKey{NodeName: m[1]}
} else if m := matchIPPool.FindStringSubmatch(path); m != nil {
log.Debugf("Path is a pool: %v", path)
mungedCIDR := m[1]
cidr := strings.Replace(mungedCIDR, "-", "/", 1)
_, c, err := net.ParseCIDR(cidr)
if err != nil {
log.WithError(err).Warningf("Failed to parse CIDR %s", cidr)
} else {
return IPPoolKey{CIDR: *c}
}
} else if m := matchGlobalConfig.FindStringSubmatch(path); m != nil {
log.Debugf("Path is a global felix config: %v", path)
return GlobalConfigKey{Name: m[1]}
} else if m := matchHostConfig.FindStringSubmatch(path); m != nil {
log.Debugf("Path is a host config: %v", path)
return HostConfigKey{Hostname: m[1], Name: m[2]}
} else if matchReadyFlag.MatchString(path) {
log.Debugf("Path is a ready flag: %v", path)
return ReadyFlagKey{}
} else if k := (NodeBGPConfigListOptions{}).KeyFromDefaultPath(path); k != nil {
return k
} else if k := (GlobalBGPConfigListOptions{}).KeyFromDefaultPath(path); k != nil {
return k
} else if k := (BlockAffinityListOptions{}).KeyFromDefaultPath(path); k != nil {
return k
} else if k := (BlockListOptions{}).KeyFromDefaultPath(path); k != nil {
return k
} else if k := (HostEndpointStatusListOptions{}).KeyFromDefaultPath(path); k != nil {
return k
} else if k := (WorkloadEndpointStatusListOptions{}).KeyFromDefaultPath(path); k != nil {
return k
} else if k := (ActiveStatusReportListOptions{}).KeyFromDefaultPath(path); k != nil {
return k
} else if k := (LastStatusReportListOptions{}).KeyFromDefaultPath(path); k != nil {
return k
} else {
log.Debugf("Path is unknown: %v", path)
}
// Not a key we know about.
return nil
}
// ParseValue parses the default JSON representation of our data into one of
// our value structs, according to the type of key. I.e. if passed a
// PolicyKey as the first parameter, it will try to parse rawData into a
// Policy struct.
func ParseValue(key Key, rawData []byte) (interface{}, error) {
valueType, err := key.valueType()
if err != nil {
return nil, err
}
if valueType == rawStringType {
return string(rawData), nil
}
if valueType == rawBoolType {
return string(rawData) == "true", nil
}
if valueType == rawIPType {
ip := net2.ParseIP(string(rawData))
if ip == nil {
return nil, nil
}
return &net.IP{IP: ip}, nil
}
value := reflect.New(valueType)
elem := value.Elem()
if elem.Kind() == reflect.Struct && elem.NumField() > 0 {
if elem.Field(0).Type() == reflect.ValueOf(key).Type() {
elem.Field(0).Set(reflect.ValueOf(key))
}
}
iface := value.Interface()
err = json.Unmarshal(rawData, iface)
if err != nil {
// This is a special case to address backwards compatibility from the time when we had no state information as block affinity value.
// example:
// Key: "/calico/ipam/v2/host/myhost.io/ipv4/block/172.29.82.0-26"
// Value: ""
// In 3.0.7 we added block affinity state as the value, so old "" value is no longer a valid JSON, so for that
// particular case we replace the "" with a "{}" so it can be parsed and we don't leak blocks after upgrade to Calico 3.0.7
// See: https://github.com/projectcalico/calico/issues/1956
if bytes.Equal(rawData, []byte(``)) && valueType == typeBlockAff {
rawData = []byte(`{}`)
if err = json.Unmarshal(rawData, iface); err != nil {
return nil, err
}
} else {
log.Warningf("Failed to unmarshal %#v into value %#v",
string(rawData), value)
return nil, err
}
}
if elem.Kind() != reflect.Struct {
// Pointer to a map or slice, unwrap.
iface = elem.Interface()
}
return iface, nil
}
// SerializeValue serializes a value in the model to a []byte to be stored in the datastore. This
// performs the opposite processing to ParseValue()
func SerializeValue(d *KVPair) ([]byte, error) {
valueType, err := d.Key.valueType()
if err != nil {
return nil, err
}
if d.Value == nil {
return json.Marshal(nil)
}
if valueType == rawStringType {
return []byte(d.Value.(string)), nil
}
if valueType == rawBoolType {
return []byte(fmt.Sprint(d.Value)), nil
}
if valueType == rawIPType {
return []byte(fmt.Sprint(d.Value)), nil
}
return json.Marshal(d.Value)
}

View File

@@ -0,0 +1,19 @@
// Copyright (c) 2021 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
const (
KindKubernetesEndpointSlice = "KubernetesEndpointSlice"
)

View File

@@ -0,0 +1,19 @@
// Copyright (c) 2021 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
const (
KindKubernetesNetworkPolicy = "KubernetesNetworkPolicy"
)

View File

@@ -0,0 +1,19 @@
// Copyright (c) 2021 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
const (
KindKubernetesService = "KubernetesService"
)

View File

@@ -0,0 +1,30 @@
// 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 "strings"
// escapeName removes any "/" from the name and URL encodes it to %2f,
// and necessarily removes % and encodes to %25.
func escapeName(name string) string {
name = strings.Replace(name, "%", "%25", -1)
return strings.Replace(name, "/", "%2f", -1)
}
// unescapeName replaces %2f and %25 in the name back to be a / and %.
func unescapeName(name string) string {
name = strings.Replace(name, "%2f", "/", -1)
return strings.Replace(name, "%25", "%", -1)
}

View File

@@ -0,0 +1,95 @@
// Copyright (c) 2017-2018 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"
log "github.com/sirupsen/logrus"
"github.com/projectcalico/calico/libcalico-go/lib/errors"
"github.com/projectcalico/calico/libcalico-go/lib/net"
)
var (
matchNetworkSet = regexp.MustCompile("^/?calico/v1/netset/([^/]+)$")
typeNetworkSet = reflect.TypeOf(NetworkSet{})
)
type NetworkSetKey struct {
Name string `json:"-" validate:"required,namespacedName"`
}
func (key NetworkSetKey) defaultPath() (string, error) {
if key.Name == "" {
return "", errors.ErrorInsufficientIdentifiers{Name: "name"}
}
e := fmt.Sprintf("/calico/v1/netset/%s", escapeName(key.Name))
return e, nil
}
func (key NetworkSetKey) defaultDeletePath() (string, error) {
return key.defaultPath()
}
func (key NetworkSetKey) defaultDeleteParentPaths() ([]string, error) {
return nil, nil
}
func (key NetworkSetKey) valueType() (reflect.Type, error) {
return typeNetworkSet, nil
}
func (key NetworkSetKey) String() string {
return fmt.Sprintf("NetworkSet(name=%s)", key.Name)
}
type NetworkSetListOptions struct {
Name string
}
func (options NetworkSetListOptions) defaultPathRoot() string {
k := "/calico/v1/netset"
if options.Name == "" {
return k
}
k = k + fmt.Sprintf("/%s", escapeName(options.Name))
return k
}
func (options NetworkSetListOptions) KeyFromDefaultPath(path string) Key {
log.Debugf("Get NetworkSet key from %s", path)
r := matchNetworkSet.FindAllStringSubmatch(path, -1)
if len(r) != 1 {
log.Debugf("Didn't match regex")
return nil
}
name := unescapeName(r[0][1])
if options.Name != "" && name != options.Name {
log.Debugf("Didn't match name %s != %s", options.Name, name)
return nil
}
return NetworkSetKey{Name: name}
}
type NetworkSet struct {
Nets []net.IPNet `json:"nets,omitempty" validate:"omitempty,dive,cidr"`
Labels map[string]string `json:"labels,omitempty" validate:"omitempty,labels"`
ProfileIDs []string `json:"profile_ids,omitempty" validate:"omitempty,dive,name"`
}

View File

@@ -0,0 +1,282 @@
// Copyright (c) 2016,2020 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 (
goerrors "errors"
"fmt"
"regexp"
"reflect"
log "github.com/sirupsen/logrus"
"github.com/projectcalico/api/pkg/lib/numorstring"
"github.com/projectcalico/calico/libcalico-go/lib/errors"
"github.com/projectcalico/calico/libcalico-go/lib/net"
)
var (
typeNode = reflect.TypeOf(Node{})
typeHostMetadata = reflect.TypeOf(HostMetadata{})
typeOrchRefs = reflect.TypeOf([]OrchRef{})
typeHostIp = rawIPType
typeWireguard = reflect.TypeOf(Wireguard{})
matchHostMetadata = regexp.MustCompile(`^/?calico/v1/host/([^/]+)/metadata$`)
matchHostIp = regexp.MustCompile(`^/?calico/v1/host/([^/]+)/bird_ip$`)
matchWireguard = regexp.MustCompile(`^/?calico/v1/host/([^/]+)/wireguard$`)
)
type Node struct {
// Felix specific configuration
FelixIPv4 *net.IP
// Node specific labels
Labels map[string]string `json:"labels,omitempty"`
// BGP specific configuration
BGPIPv4Addr *net.IP
BGPIPv6Addr *net.IP
BGPIPv4Net *net.IPNet
BGPIPv6Net *net.IPNet
BGPASNumber *numorstring.ASNumber
OrchRefs []OrchRef `json:"orchRefs,omitempty"`
}
type OrchRef struct {
Orchestrator string `json:"orchestrator,omitempty"`
NodeName string `json:"nodeName,omitempty"`
}
type Wireguard struct {
InterfaceIPv4Addr *net.IP `json:"interfaceIPv4Addr,omitempty"`
PublicKey string `json:"publicKey,omitempty"`
InterfaceIPv6Addr *net.IP `json:"interfaceIPv6Addr,omitempty"`
PublicKeyV6 string `json:"publicKeyV6,omitempty"`
}
type NodeKey struct {
Hostname string
}
func (key NodeKey) defaultPath() (string, error) {
return "", goerrors.New("Node is a composite type, so not handled with a single path")
}
func (key NodeKey) defaultDeletePath() (string, error) {
return "", goerrors.New("Node is a composite type, so not handled with a single path")
}
func (key NodeKey) defaultDeleteParentPaths() ([]string, error) {
return nil, goerrors.New("Node is composite type, so not handled with a single path")
}
func (key NodeKey) valueType() (reflect.Type, error) {
return typeNode, nil
}
func (key NodeKey) String() string {
return fmt.Sprintf("Node(name=%s)", key.Hostname)
}
type NodeListOptions struct {
Hostname string
}
func (options NodeListOptions) defaultPathRoot() string {
return ""
}
func (options NodeListOptions) KeyFromDefaultPath(path string) Key {
return nil
}
// The node is a composite of the following subcomponents:
// - The host metadata. This is the primary subcomponent and is used to enumerate
// hosts. However, for backwards compatibility, the etcd driver needs to handle
// that this may not exist, and instead need to enumerate based on directory.
// - The host IPv4 address used by Calico to lock down IPIP traffic.
// - The BGP IPv4 and IPv6 addresses
// - The BGP ASN.
type HostMetadata struct {
}
type HostMetadataKey struct {
Hostname string
}
func (key HostMetadataKey) defaultPath() (string, error) {
if key.Hostname == "" {
return "", errors.ErrorInsufficientIdentifiers{Name: "name"}
}
return fmt.Sprintf("/calico/v1/host/%s/metadata", key.Hostname), nil
}
func (key HostMetadataKey) defaultDeletePath() (string, error) {
if key.Hostname == "" {
return "", errors.ErrorInsufficientIdentifiers{Name: "name"}
}
return fmt.Sprintf("/calico/v1/host/%s", key.Hostname), nil
}
func (key HostMetadataKey) defaultDeleteParentPaths() ([]string, error) {
return nil, nil
}
func (key HostMetadataKey) valueType() (reflect.Type, error) {
return typeHostMetadata, nil
}
func (key HostMetadataKey) String() string {
return fmt.Sprintf("Node(name=%s)", key.Hostname)
}
type HostMetadataListOptions struct {
Hostname string
}
func (options HostMetadataListOptions) defaultPathRoot() string {
if options.Hostname == "" {
return "/calico/v1/host"
} else {
return fmt.Sprintf("/calico/v1/host/%s/metadata", options.Hostname)
}
}
func (options HostMetadataListOptions) KeyFromDefaultPath(path string) Key {
log.Debugf("Get Node key from %s", path)
if r := matchHostMetadata.FindAllStringSubmatch(path, -1); len(r) == 1 {
return HostMetadataKey{Hostname: r[0][1]}
} else {
log.Debugf("%s didn't match regex", path)
return nil
}
}
// The Felix Host IP Key.
type HostIPKey struct {
Hostname string
}
func (key HostIPKey) defaultPath() (string, error) {
return fmt.Sprintf("/calico/v1/host/%s/bird_ip",
key.Hostname), nil
}
func (key HostIPKey) defaultDeletePath() (string, error) {
return key.defaultPath()
}
func (key HostIPKey) defaultDeleteParentPaths() ([]string, error) {
return nil, nil
}
func (key HostIPKey) valueType() (reflect.Type, error) {
return typeHostIp, nil
}
func (key HostIPKey) String() string {
return fmt.Sprintf("Node(name=%s)", key.Hostname)
}
type OrchRefKey struct {
Hostname string
}
func (key OrchRefKey) defaultPath() (string, error) {
return fmt.Sprintf("/calico/v1/host/%s/orchestrator_refs",
key.Hostname), nil
}
func (key OrchRefKey) defaultDeletePath() (string, error) {
return key.defaultPath()
}
func (key OrchRefKey) defaultDeleteParentPaths() ([]string, error) {
return nil, nil
}
func (key OrchRefKey) valueType() (reflect.Type, error) {
return typeOrchRefs, nil
}
func (key OrchRefKey) String() string {
return fmt.Sprintf("OrchRefs(nodename=%s)", key.Hostname)
}
type OrchRefListOptions struct {
Hostname string
}
func (options OrchRefListOptions) defaultPathRoot() string {
return fmt.Sprintf("/calico/v1/host/%s/orchestrator_refs", options.Hostname)
}
func (options OrchRefListOptions) KeyFromDefaultPath(path string) Key {
return OrchRefKey{Hostname: options.Hostname}
}
// The Felix Wireguard Key.
type WireguardKey struct {
NodeName string
}
func (key WireguardKey) defaultPath() (string, error) {
if key.NodeName == "" {
return "", errors.ErrorInsufficientIdentifiers{Name: "name"}
}
return fmt.Sprintf("/calico/v1/host/%s/wireguard",
key.NodeName), nil
}
func (key WireguardKey) defaultDeletePath() (string, error) {
return key.defaultPath()
}
func (key WireguardKey) defaultDeleteParentPaths() ([]string, error) {
return nil, nil
}
func (key WireguardKey) valueType() (reflect.Type, error) {
return typeWireguard, nil
}
func (key WireguardKey) String() string {
return fmt.Sprintf("Node(nodename=%s)", key.NodeName)
}
type WireguardListOptions struct {
NodeName string
}
func (options WireguardListOptions) defaultPathRoot() string {
if options.NodeName == "" {
return "/calico/v1/host"
} else {
return fmt.Sprintf("/calico/v1/host/%s/wireguard", options.NodeName)
}
}
func (options WireguardListOptions) KeyFromDefaultPath(path string) Key {
log.Debugf("Get Node key from %s", path)
if r := matchWireguard.FindAllStringSubmatch(path, -1); len(r) == 1 {
return WireguardKey{NodeName: r[0][1]}
} else {
log.Debugf("%s didn't match regex", path)
return nil
}
}

View File

@@ -0,0 +1,126 @@
// Copyright (c) 2016-2018 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"
"strings"
log "github.com/sirupsen/logrus"
"github.com/projectcalico/calico/libcalico-go/lib/errors"
)
var (
matchPolicy = regexp.MustCompile("^/?calico/v1/policy/tier/([^/]+)/policy/([^/]+)$")
typePolicy = reflect.TypeOf(Policy{})
)
type PolicyKey struct {
Name string `json:"-" validate:"required,name"`
}
func (key PolicyKey) defaultPath() (string, error) {
if key.Name == "" {
return "", errors.ErrorInsufficientIdentifiers{Name: "name"}
}
e := fmt.Sprintf("/calico/v1/policy/tier/default/policy/%s",
escapeName(key.Name))
return e, nil
}
func (key PolicyKey) defaultDeletePath() (string, error) {
return key.defaultPath()
}
func (key PolicyKey) defaultDeleteParentPaths() ([]string, error) {
return nil, nil
}
func (key PolicyKey) valueType() (reflect.Type, error) {
return typePolicy, nil
}
func (key PolicyKey) String() string {
return fmt.Sprintf("Policy(name=%s)", key.Name)
}
type PolicyListOptions struct {
Name string
}
func (options PolicyListOptions) defaultPathRoot() string {
k := "/calico/v1/policy/tier/default/policy"
if options.Name == "" {
return k
}
k = k + fmt.Sprintf("/%s", escapeName(options.Name))
return k
}
func (options PolicyListOptions) KeyFromDefaultPath(path string) Key {
log.Debugf("Get Policy key from %s", path)
r := matchPolicy.FindAllStringSubmatch(path, -1)
if len(r) != 1 {
log.Debugf("Didn't match regex")
return nil
}
name := unescapeName(r[0][2])
if options.Name != "" && name != options.Name {
log.Debugf("Didn't match name %s != %s", options.Name, name)
return nil
}
return PolicyKey{Name: name}
}
type Policy struct {
Namespace string `json:"namespace,omitempty" validate:"omitempty"`
Order *float64 `json:"order,omitempty" validate:"omitempty"`
InboundRules []Rule `json:"inbound_rules,omitempty" validate:"omitempty,dive"`
OutboundRules []Rule `json:"outbound_rules,omitempty" validate:"omitempty,dive"`
Selector string `json:"selector" validate:"selector"`
DoNotTrack bool `json:"untracked,omitempty"`
Annotations map[string]string `json:"annotations,omitempty"`
PreDNAT bool `json:"pre_dnat,omitempty"`
ApplyOnForward bool `json:"apply_on_forward,omitempty"`
Types []string `json:"types,omitempty"`
}
func (p Policy) String() string {
parts := make([]string, 0)
if p.Order != nil {
parts = append(parts, fmt.Sprintf("order:%v", *p.Order))
}
parts = append(parts, fmt.Sprintf("selector:%#v", p.Selector))
inRules := make([]string, len(p.InboundRules))
for ii, rule := range p.InboundRules {
inRules[ii] = rule.String()
}
parts = append(parts, fmt.Sprintf("inbound:%v", strings.Join(inRules, ";")))
outRules := make([]string, len(p.OutboundRules))
for ii, rule := range p.OutboundRules {
outRules[ii] = rule.String()
}
parts = append(parts, fmt.Sprintf("outbound:%v", strings.Join(outRules, ";")))
parts = append(parts, fmt.Sprintf("untracked:%v", p.DoNotTrack))
parts = append(parts, fmt.Sprintf("pre_dnat:%v", p.PreDNAT))
parts = append(parts, fmt.Sprintf("apply_on_forward:%v", p.ApplyOnForward))
parts = append(parts, fmt.Sprintf("types:%v", strings.Join(p.Types, ";")))
return strings.Join(parts, ",")
}

View File

@@ -0,0 +1,208 @@
// 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"
log "github.com/sirupsen/logrus"
"github.com/projectcalico/calico/libcalico-go/lib/errors"
)
var (
matchProfile = regexp.MustCompile("^/?calico/v1/policy/profile/([^/]+)/(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)
}
// 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 "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 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 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
}

View File

@@ -0,0 +1,34 @@
// Copyright (c) 2018 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"
const (
NoRegion string = "no-region"
RegionPrefix string = "region-"
)
func RegionString(region string) string {
if region != "" {
return RegionPrefix + region
} else {
return NoRegion
}
}
func ErrorSlashInRegionString(regionString string) error {
return fmt.Errorf("RegionString %v is invalid because it includes a slash", regionString)
}

View File

@@ -0,0 +1,321 @@
// Copyright (c) 2016-2021 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"
"reflect"
"regexp"
"strings"
log "github.com/sirupsen/logrus"
kapiv1 "k8s.io/api/core/v1"
discovery "k8s.io/api/discovery/v1"
apiv3 "github.com/projectcalico/api/pkg/apis/projectcalico/v3"
libapiv3 "github.com/projectcalico/calico/libcalico-go/lib/apis/v3"
"github.com/projectcalico/calico/libcalico-go/lib/namespace"
)
// Name/type information about a single resource.
type resourceInfo struct {
typeOf reflect.Type
plural string
kindLower string
kind string
}
var (
matchGlobalResource = regexp.MustCompile("^/calico/resources/v3/projectcalico[.]org/([^/]+)/([^/]+)$")
matchNamespacedResource = regexp.MustCompile("^/calico/resources/v3/projectcalico[.]org/([^/]+)/([^/]+)/([^/]+)$")
resourceInfoByKindLower = make(map[string]resourceInfo)
resourceInfoByPlural = make(map[string]resourceInfo)
)
func registerResourceInfo(kind string, plural string, typeOf reflect.Type) {
kindLower := strings.ToLower(kind)
plural = strings.ToLower(plural)
ri := resourceInfo{
typeOf: typeOf,
kindLower: kindLower,
kind: kind,
plural: plural,
}
resourceInfoByKindLower[kindLower] = ri
resourceInfoByPlural[plural] = ri
}
func init() {
registerResourceInfo(
apiv3.KindBGPPeer,
"bgppeers",
reflect.TypeOf(apiv3.BGPPeer{}),
)
registerResourceInfo(
apiv3.KindBGPConfiguration,
"bgpconfigurations",
reflect.TypeOf(apiv3.BGPConfiguration{}),
)
registerResourceInfo(
apiv3.KindClusterInformation,
"clusterinformations",
reflect.TypeOf(apiv3.ClusterInformation{}),
)
registerResourceInfo(
apiv3.KindFelixConfiguration,
"felixconfigurations",
reflect.TypeOf(apiv3.FelixConfiguration{}),
)
registerResourceInfo(
apiv3.KindGlobalNetworkPolicy,
"globalnetworkpolicies",
reflect.TypeOf(apiv3.GlobalNetworkPolicy{}),
)
registerResourceInfo(
apiv3.KindHostEndpoint,
"hostendpoints",
reflect.TypeOf(apiv3.HostEndpoint{}),
)
registerResourceInfo(
apiv3.KindGlobalNetworkSet,
"globalnetworksets",
reflect.TypeOf(apiv3.GlobalNetworkSet{}),
)
registerResourceInfo(
apiv3.KindIPPool,
"ippools",
reflect.TypeOf(apiv3.IPPool{}),
)
registerResourceInfo(
apiv3.KindIPReservation,
"ipreservations",
reflect.TypeOf(apiv3.IPReservation{}),
)
registerResourceInfo(
apiv3.KindNetworkPolicy,
"networkpolicies",
reflect.TypeOf(apiv3.NetworkPolicy{}),
)
registerResourceInfo(
KindKubernetesNetworkPolicy,
"kubernetesnetworkpolicies",
reflect.TypeOf(apiv3.NetworkPolicy{}),
)
registerResourceInfo(
KindKubernetesEndpointSlice,
"kubernetesendpointslices",
reflect.TypeOf(discovery.EndpointSlice{}),
)
registerResourceInfo(
apiv3.KindNetworkSet,
"networksets",
reflect.TypeOf(apiv3.NetworkSet{}),
)
registerResourceInfo(
libapiv3.KindNode,
"nodes",
reflect.TypeOf(libapiv3.Node{}),
)
registerResourceInfo(
apiv3.KindCalicoNodeStatus,
"caliconodestatuses",
reflect.TypeOf(apiv3.CalicoNodeStatus{}),
)
registerResourceInfo(
apiv3.KindProfile,
"profiles",
reflect.TypeOf(apiv3.Profile{}),
)
registerResourceInfo(
libapiv3.KindWorkloadEndpoint,
"workloadendpoints",
reflect.TypeOf(libapiv3.WorkloadEndpoint{}),
)
registerResourceInfo(
libapiv3.KindIPAMConfig,
"ipamconfigs",
reflect.TypeOf(libapiv3.IPAMConfig{}),
)
registerResourceInfo(
apiv3.KindKubeControllersConfiguration,
"kubecontrollersconfigurations",
reflect.TypeOf(apiv3.KubeControllersConfiguration{}))
registerResourceInfo(
KindKubernetesService,
"kubernetesservice",
reflect.TypeOf(kapiv1.Service{}),
)
registerResourceInfo(
libapiv3.KindBlockAffinity,
"blockaffinities",
reflect.TypeOf(libapiv3.BlockAffinity{}),
)
registerResourceInfo(
apiv3.KindBGPFilter,
"BGPFilters",
reflect.TypeOf(apiv3.BGPFilter{}),
)
}
type ResourceKey struct {
// The name of the resource.
Name string
// The namespace of the resource. Not required if the resource is not namespaced.
Namespace string
// The resource kind.
Kind string
}
func (key ResourceKey) defaultPath() (string, error) {
return key.defaultDeletePath()
}
func (key ResourceKey) defaultDeletePath() (string, error) {
ri, ok := resourceInfoByKindLower[strings.ToLower(key.Kind)]
if !ok {
return "", fmt.Errorf("couldn't convert key: %+v", key)
}
if namespace.IsNamespaced(key.Kind) {
return fmt.Sprintf("/calico/resources/v3/projectcalico.org/%s/%s/%s", ri.plural, key.Namespace, key.Name), nil
}
return fmt.Sprintf("/calico/resources/v3/projectcalico.org/%s/%s", ri.plural, key.Name), nil
}
func (key ResourceKey) defaultDeleteParentPaths() ([]string, error) {
return nil, nil
}
func (key ResourceKey) valueType() (reflect.Type, error) {
ri, ok := resourceInfoByKindLower[strings.ToLower(key.Kind)]
if !ok {
return nil, fmt.Errorf("Unexpected resource kind: " + key.Kind)
}
return ri.typeOf, nil
}
func (key ResourceKey) String() string {
if namespace.IsNamespaced(key.Kind) {
return fmt.Sprintf("%s(%s/%s)", key.Kind, key.Namespace, key.Name)
}
return fmt.Sprintf("%s(%s)", key.Kind, key.Name)
}
type ResourceListOptions struct {
// The name of the resource.
Name string
// The namespace of the resource. Not required if the resource is not namespaced.
Namespace string
// The resource kind.
Kind string
// Whether the name is prefix rather than the full name.
Prefix bool
}
// If the Kind, Namespace and Name are specified, but the Name is a prefix then the
// last segment of this path is a prefix.
func (options ResourceListOptions) IsLastSegmentIsPrefix() bool {
return len(options.Kind) != 0 &&
(len(options.Namespace) != 0 || !namespace.IsNamespaced(options.Kind)) &&
len(options.Name) != 0 &&
options.Prefix
}
func (options ResourceListOptions) KeyFromDefaultPath(path string) Key {
ri, ok := resourceInfoByKindLower[strings.ToLower(options.Kind)]
if !ok {
log.Panic("Unexpected resource kind: " + options.Kind)
}
if namespace.IsNamespaced(options.Kind) {
log.Debugf("Get Namespaced Resource key from %s", path)
r := matchNamespacedResource.FindAllStringSubmatch(path, -1)
if len(r) != 1 {
log.Debugf("Didn't match regex")
return nil
}
kindPlural := r[0][1]
namespace := r[0][2]
name := r[0][3]
if len(options.Kind) == 0 {
panic("Kind must be specified in List option but is not")
}
if kindPlural != ri.plural {
log.Debugf("Didn't match kind %s != %s", kindPlural, kindPlural)
return nil
}
if len(options.Namespace) != 0 && namespace != options.Namespace {
log.Debugf("Didn't match namespace %s != %s", options.Namespace, namespace)
return nil
}
if len(options.Name) != 0 {
if options.Prefix && !strings.HasPrefix(name, options.Name) {
log.Debugf("Didn't match name prefix %s != prefix(%s)", options.Name, name)
return nil
} else if !options.Prefix && name != options.Name {
log.Debugf("Didn't match name %s != %s", options.Name, name)
return nil
}
}
return ResourceKey{Kind: options.Kind, Namespace: namespace, Name: name}
}
log.Debugf("Get Global Resource key from %s", path)
r := matchGlobalResource.FindAllStringSubmatch(path, -1)
if len(r) != 1 {
log.Debugf("Didn't match regex")
return nil
}
kindPlural := r[0][1]
name := r[0][2]
if kindPlural != ri.plural {
log.Debugf("Didn't match kind %s != %s", kindPlural, ri.plural)
return nil
}
if len(options.Name) != 0 {
if options.Prefix && !strings.HasPrefix(name, options.Name) {
log.Debugf("Didn't match name prefix %s != prefix(%s)", options.Name, name)
return nil
} else if !options.Prefix && name != options.Name {
log.Debugf("Didn't match name %s != %s", options.Name, name)
return nil
}
}
return ResourceKey{Kind: options.Kind, Name: name}
}
func (options ResourceListOptions) defaultPathRoot() string {
ri, ok := resourceInfoByKindLower[strings.ToLower(options.Kind)]
if !ok {
log.Panic("Unexpected resource kind: " + options.Kind)
}
k := "/calico/resources/v3/projectcalico.org/" + ri.plural
if namespace.IsNamespaced(options.Kind) {
if options.Namespace == "" {
return k
}
k = k + "/" + options.Namespace
}
if options.Name == "" {
return k
}
return k + "/" + options.Name
}
func (options ResourceListOptions) String() string {
return options.Kind
}

View File

@@ -0,0 +1,268 @@
// Copyright (c) 2016-2018 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"
"strconv"
"strings"
apiv3 "github.com/projectcalico/api/pkg/apis/projectcalico/v3"
"github.com/projectcalico/api/pkg/lib/numorstring"
"github.com/projectcalico/calico/libcalico-go/lib/net"
)
type Rule struct {
Action string `json:"action,omitempty"`
IPVersion *int `json:"ip_version,omitempty" validate:"omitempty,ipVersion"`
Protocol *numorstring.Protocol `json:"protocol,omitempty" validate:"omitempty"`
NotProtocol *numorstring.Protocol `json:"!protocol,omitempty" validate:"omitempty"`
// ICMP validation notes: 0 is a valid (common) ICMP type and code. Type = 255 is not assigned
// to any protocol and the Linux kernel doesn't support matching on it so we validate against
// it.
ICMPType *int `json:"icmp_type,omitempty" validate:"omitempty,gte=0,lt=255"`
ICMPCode *int `json:"icmp_code,omitempty" validate:"omitempty,gte=0,lte=255"`
NotICMPType *int `json:"!icmp_type,omitempty" validate:"omitempty,gte=0,lt=255"`
NotICMPCode *int `json:"!icmp_code,omitempty" validate:"omitempty,gte=0,lte=255"`
SrcTag string `json:"src_tag,omitempty" validate:"omitempty,tag"`
SrcNet *net.IPNet `json:"src_net,omitempty" validate:"omitempty"`
SrcNets []*net.IPNet `json:"src_nets,omitempty" validate:"omitempty"`
SrcSelector string `json:"src_selector,omitempty" validate:"omitempty,selector"`
SrcPorts []numorstring.Port `json:"src_ports,omitempty" validate:"omitempty,dive"`
SrcService string `json:"src_service,omitempty" validate:"omitempty"`
SrcServiceNamespace string `json:"src_service_ns,omitempty" validate:"omitempty"`
DstTag string `json:"dst_tag,omitempty" validate:"omitempty,tag"`
DstSelector string `json:"dst_selector,omitempty" validate:"omitempty,selector"`
DstNet *net.IPNet `json:"dst_net,omitempty" validate:"omitempty"`
DstNets []*net.IPNet `json:"dst_nets,omitempty" validate:"omitempty"`
DstPorts []numorstring.Port `json:"dst_ports,omitempty" validate:"omitempty,dive"`
DstService string `json:"dst_service,omitempty" validate:"omitempty"`
DstServiceNamespace string `json:"dst_service_ns,omitempty" validate:"omitempty"`
NotSrcTag string `json:"!src_tag,omitempty" validate:"omitempty,tag"`
NotSrcNet *net.IPNet `json:"!src_net,omitempty" validate:"omitempty"`
NotSrcNets []*net.IPNet `json:"!src_nets,omitempty" validate:"omitempty"`
NotSrcSelector string `json:"!src_selector,omitempty" validate:"omitempty,selector"`
NotSrcPorts []numorstring.Port `json:"!src_ports,omitempty" validate:"omitempty,dive"`
NotDstTag string `json:"!dst_tag,omitempty" validate:"omitempty"`
NotDstSelector string `json:"!dst_selector,omitempty" validate:"omitempty,selector"`
NotDstNet *net.IPNet `json:"!dst_net,omitempty" validate:"omitempty"`
NotDstNets []*net.IPNet `json:"!dst_nets,omitempty" validate:"omitempty"`
NotDstPorts []numorstring.Port `json:"!dst_ports,omitempty" validate:"omitempty,dive"`
// These fields allow us to pass through the raw match criteria from the V3 datamodel unmodified.
// The selectors above are formed in the update processor layer by combining the original
// selectors, namespace selectors and service account selectors into one.
OriginalSrcSelector string `json:"orig_src_selector,omitempty" validate:"omitempty,selector"`
OriginalSrcNamespaceSelector string `json:"orig_src_namespace_selector,omitempty" validate:"omitempty,selector"`
OriginalDstSelector string `json:"orig_dst_selector,omitempty" validate:"omitempty,selector"`
OriginalDstNamespaceSelector string `json:"orig_dst_namespace_selector,omitempty" validate:"omitempty,selector"`
OriginalNotSrcSelector string `json:"!orig_src_selector,omitempty" validate:"omitempty,selector"`
OriginalNotDstSelector string `json:"!orig_dst_selector,omitempty" validate:"omitempty,selector"`
OriginalSrcServiceAccountNames []string `json:"orig_src_service_acct_names,omitempty" validate:"omitempty"`
OriginalSrcServiceAccountSelector string `json:"orig_src_service_acct_selector,omitempty" validate:"omitempty,selector"`
OriginalDstServiceAccountNames []string `json:"orig_dst_service_acct_names,omitempty" validate:"omitempty"`
OriginalDstServiceAccountSelector string `json:"orig_dst_service_acct_selector,omitempty" validate:"omitempty,selector"`
// These fields allow us to pass through application layer selectors from the V3 datamodel.
HTTPMatch *HTTPMatch `json:"http,omitempty" validate:"omitempty"`
LogPrefix string `json:"log_prefix,omitempty" validate:"omitempty"`
Metadata *RuleMetadata `json:"metadata,omitempty" validate:"omitempty"`
}
type HTTPMatch struct {
Methods []string `json:"methods,omitempty" validate:"omitempty"`
Paths []apiv3.HTTPPath `json:"paths,omitempty" validate:"omitempty"`
}
type RuleMetadata struct {
Annotations map[string]string `json:"annotations,omitempty"`
}
func combineNets(n *net.IPNet, nets []*net.IPNet) []*net.IPNet {
if n == nil {
return nets
}
if len(nets) == 0 {
return []*net.IPNet{n}
}
var combination = make([]*net.IPNet, len(nets)+1)
copy(combination, nets)
combination[len(nets)] = n
return combination
}
func (r Rule) AllSrcNets() []*net.IPNet {
return combineNets(r.SrcNet, r.SrcNets)
}
func (r Rule) AllDstNets() []*net.IPNet {
return combineNets(r.DstNet, r.DstNets)
}
func (r Rule) AllNotSrcNets() []*net.IPNet {
return combineNets(r.NotSrcNet, r.NotSrcNets)
}
func (r Rule) AllNotDstNets() []*net.IPNet {
return combineNets(r.NotDstNet, r.NotDstNets)
}
func joinNets(nets []*net.IPNet) string {
parts := make([]string, len(nets))
for i, n := range nets {
parts[i] = n.String()
}
return strings.Join(parts, ",")
}
func (r Rule) String() string {
parts := make([]string, 0)
// Action.
if r.Action != "" {
parts = append(parts, r.Action)
} else {
parts = append(parts, "Allow")
}
// Global packet attributes that don't depend on direction.
if r.Protocol != nil {
parts = append(parts, r.Protocol.String())
}
if r.NotProtocol != nil {
parts = append(parts, "!"+r.NotProtocol.String())
}
if r.ICMPType != nil {
parts = append(parts, "type", strconv.Itoa(*r.ICMPType))
}
if r.ICMPCode != nil {
parts = append(parts, "code", strconv.Itoa(*r.ICMPCode))
}
if r.NotICMPType != nil {
parts = append(parts, "!type", strconv.Itoa(*r.NotICMPType))
}
if r.NotICMPCode != nil {
parts = append(parts, "!code", strconv.Itoa(*r.NotICMPCode))
}
{
// Source attributes. New block ensures that fromParts goes out-of-scope before
// we calculate toParts. This prevents copy/paste errors.
fromParts := make([]string, 0)
if len(r.SrcPorts) > 0 {
srcPorts := make([]string, len(r.SrcPorts))
for ii, port := range r.SrcPorts {
srcPorts[ii] = port.String()
}
fromParts = append(fromParts, "ports", strings.Join(srcPorts, ","))
}
if r.SrcTag != "" {
fromParts = append(fromParts, "tag", r.SrcTag)
}
if r.SrcSelector != "" {
fromParts = append(fromParts, "selector", fmt.Sprintf("%#v", r.SrcSelector))
}
srcNets := r.AllSrcNets()
if len(srcNets) != 0 {
fromParts = append(fromParts, "cidr", joinNets(srcNets))
}
if len(r.NotSrcPorts) > 0 {
notSrcPorts := make([]string, len(r.NotSrcPorts))
for ii, port := range r.NotSrcPorts {
notSrcPorts[ii] = port.String()
}
fromParts = append(fromParts, "!ports", strings.Join(notSrcPorts, ","))
}
if r.NotSrcTag != "" {
fromParts = append(fromParts, "!tag", r.NotSrcTag)
}
if r.NotSrcSelector != "" {
fromParts = append(fromParts, "!selector", fmt.Sprintf("%#v", r.NotSrcSelector))
}
notSrcNets := r.AllNotSrcNets()
if len(notSrcNets) != 0 {
fromParts = append(fromParts, "!cidr", joinNets(notSrcNets))
}
if len(fromParts) > 0 {
parts = append(parts, "from")
parts = append(parts, fromParts...)
}
}
{
// Destination attributes.
toParts := make([]string, 0)
if len(r.DstPorts) > 0 {
DstPorts := make([]string, len(r.DstPorts))
for ii, port := range r.DstPorts {
DstPorts[ii] = port.String()
}
toParts = append(toParts, "ports", strings.Join(DstPorts, ","))
}
if r.DstTag != "" {
toParts = append(toParts, "tag", r.DstTag)
}
if r.DstSelector != "" {
toParts = append(toParts, "selector", fmt.Sprintf("%#v", r.DstSelector))
}
dstNets := r.AllDstNets()
if len(dstNets) != 0 {
toParts = append(toParts, "cidr", joinNets(dstNets))
}
if len(r.NotDstPorts) > 0 {
notDstPorts := make([]string, len(r.NotDstPorts))
for ii, port := range r.NotDstPorts {
notDstPorts[ii] = port.String()
}
toParts = append(toParts, "!ports", strings.Join(notDstPorts, ","))
}
if r.NotDstTag != "" {
toParts = append(toParts, "!tag", r.NotDstTag)
}
if r.NotDstSelector != "" {
toParts = append(toParts, "!selector", fmt.Sprintf("%#v", r.NotDstSelector))
}
notDstNets := r.AllNotDstNets()
if len(notDstNets) != 0 {
toParts = append(toParts, "!cidr", joinNets(notDstNets))
}
// HTTPMatch are destination rules.
if r.HTTPMatch != nil {
if len(r.HTTPMatch.Methods) > 0 {
toParts = append(toParts, "httpMethods", fmt.Sprintf("%+v", r.HTTPMatch.Methods))
}
if len(r.HTTPMatch.Paths) > 0 {
toParts = append(toParts, "httpPaths", fmt.Sprintf("%+v", r.HTTPMatch.Paths))
}
}
if len(toParts) > 0 {
parts = append(parts, "to")
parts = append(parts, toParts...)
}
}
return strings.Join(parts, " ")
}

View File

@@ -0,0 +1,184 @@
// Copyright (c) 2016-2018 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"
"reflect"
"regexp"
"strings"
log "github.com/sirupsen/logrus"
"github.com/projectcalico/calico/libcalico-go/lib/errors"
)
var (
matchActiveStatusReport = regexp.MustCompile("^/?calico/felix/v2/([^/]+)/host/([^/]+)/status$")
matchLastStatusReport = regexp.MustCompile("^/?calico/felix/v2/([^/]+)/host/([^/]+)/last_reported_status")
typeStatusReport = reflect.TypeOf(StatusReport{})
)
type ActiveStatusReportKey struct {
Hostname string `json:"-" validate:"required,hostname"`
RegionString string
}
func (key ActiveStatusReportKey) defaultPath() (string, error) {
return key.defaultDeletePath()
}
func (key ActiveStatusReportKey) defaultDeletePath() (string, error) {
if key.Hostname == "" {
return "", errors.ErrorInsufficientIdentifiers{Name: "hostname"}
}
if key.RegionString == "" {
return "", errors.ErrorInsufficientIdentifiers{Name: "regionString"}
}
if strings.Contains(key.RegionString, "/") {
return "", ErrorSlashInRegionString(key.RegionString)
}
e := fmt.Sprintf("/calico/felix/v2/%s/host/%s/status", key.RegionString, key.Hostname)
return e, nil
}
func (key ActiveStatusReportKey) defaultDeleteParentPaths() ([]string, error) {
return nil, nil
}
func (key ActiveStatusReportKey) valueType() (reflect.Type, error) {
return typeStatusReport, nil
}
func (key ActiveStatusReportKey) String() string {
return fmt.Sprintf("StatusReport(hostname=%s)", key.Hostname)
}
type ActiveStatusReportListOptions struct {
Hostname string
RegionString string
}
func (options ActiveStatusReportListOptions) defaultPathRoot() string {
k := "/calico/felix/v2/"
if options.RegionString == "" {
return k
}
k = k + options.RegionString + "/host"
if options.Hostname == "" {
return k
}
k = k + fmt.Sprintf("/%s/status", options.Hostname)
return k
}
func (options ActiveStatusReportListOptions) KeyFromDefaultPath(ekey string) Key {
log.Debugf("Get StatusReport key from %s", ekey)
r := matchActiveStatusReport.FindAllStringSubmatch(ekey, -1)
if len(r) != 1 {
log.Debugf("Didn't match regex")
return nil
}
regionString := r[0][1]
name := r[0][2]
if options.RegionString != "" && regionString != options.RegionString {
log.Debugf("Didn't match region %s != %s", options.RegionString, regionString)
return nil
}
if options.Hostname != "" && name != options.Hostname {
log.Debugf("Didn't match name %s != %s", options.Hostname, name)
return nil
}
return ActiveStatusReportKey{Hostname: name, RegionString: regionString}
}
type LastStatusReportKey struct {
Hostname string `json:"-" validate:"required,hostname"`
RegionString string
}
func (key LastStatusReportKey) defaultPath() (string, error) {
return key.defaultDeletePath()
}
func (key LastStatusReportKey) defaultDeletePath() (string, error) {
if key.Hostname == "" {
return "", errors.ErrorInsufficientIdentifiers{Name: "hostname"}
}
if key.RegionString == "" {
return "", errors.ErrorInsufficientIdentifiers{Name: "regionString"}
}
if strings.Contains(key.RegionString, "/") {
return "", ErrorSlashInRegionString(key.RegionString)
}
e := fmt.Sprintf("/calico/felix/v2/%s/host/%s/last_reported_status", key.RegionString, key.Hostname)
return e, nil
}
func (key LastStatusReportKey) defaultDeleteParentPaths() ([]string, error) {
return nil, nil
}
func (key LastStatusReportKey) valueType() (reflect.Type, error) {
return typeStatusReport, nil
}
func (key LastStatusReportKey) String() string {
return fmt.Sprintf("StatusReport(hostname=%s)", key.Hostname)
}
type LastStatusReportListOptions struct {
Hostname string
RegionString string
}
func (options LastStatusReportListOptions) defaultPathRoot() string {
k := "/calico/felix/v2/"
if options.RegionString == "" {
return k
}
k = k + options.RegionString + "/host"
if options.Hostname == "" {
return k
}
k = k + fmt.Sprintf("/%s/last_reported_status", options.Hostname)
return k
}
func (options LastStatusReportListOptions) KeyFromDefaultPath(ekey string) Key {
log.Debugf("Get StatusReport key from %s", ekey)
r := matchLastStatusReport.FindAllStringSubmatch(ekey, -1)
if len(r) != 1 {
log.Debugf("Didn't match regex")
return nil
}
regionString := r[0][1]
name := r[0][2]
if options.RegionString != "" && regionString != options.RegionString {
log.Debugf("Didn't match region %s != %s", options.RegionString, regionString)
return nil
}
if options.Hostname != "" && name != options.Hostname {
log.Debugf("Didn't match name %s != %s", options.Hostname, name)
return nil
}
return LastStatusReportKey{Hostname: name, RegionString: regionString}
}
type StatusReport struct {
Timestamp string `json:"time"`
UptimeSeconds float64 `json:"uptime"`
FirstUpdate bool `json:"first_update"`
}

View File

@@ -0,0 +1,185 @@
// Copyright (c) 2016-2017 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"
log "github.com/sirupsen/logrus"
"github.com/projectcalico/api/pkg/lib/numorstring"
"github.com/projectcalico/calico/libcalico-go/lib/errors"
"github.com/projectcalico/calico/libcalico-go/lib/net"
)
var (
matchWorkloadEndpoint = regexp.MustCompile("^/?calico/v1/host/([^/]+)/workload/([^/]+)/([^/]+)/endpoint/([^/]+)$")
)
type WorkloadEndpointKey struct {
Hostname string `json:"-"`
OrchestratorID string `json:"-"`
WorkloadID string `json:"-"`
EndpointID string `json:"-"`
}
func (key WorkloadEndpointKey) defaultPath() (string, error) {
if key.Hostname == "" {
return "", errors.ErrorInsufficientIdentifiers{Name: "node"}
}
if key.OrchestratorID == "" {
return "", errors.ErrorInsufficientIdentifiers{Name: "orchestrator"}
}
if key.WorkloadID == "" {
return "", errors.ErrorInsufficientIdentifiers{Name: "workload"}
}
if key.EndpointID == "" {
return "", errors.ErrorInsufficientIdentifiers{Name: "name"}
}
return fmt.Sprintf("/calico/v1/host/%s/workload/%s/%s/endpoint/%s",
key.Hostname, escapeName(key.OrchestratorID), escapeName(key.WorkloadID), escapeName(key.EndpointID)), nil
}
func (key WorkloadEndpointKey) defaultDeletePath() (string, error) {
return key.defaultPath()
}
func (key WorkloadEndpointKey) defaultDeleteParentPaths() ([]string, error) {
if key.Hostname == "" {
return nil, errors.ErrorInsufficientIdentifiers{Name: "node"}
}
if key.OrchestratorID == "" {
return nil, errors.ErrorInsufficientIdentifiers{Name: "orchestrator"}
}
if key.WorkloadID == "" {
return nil, errors.ErrorInsufficientIdentifiers{Name: "workload"}
}
workload := fmt.Sprintf("/calico/v1/host/%s/workload/%s/%s",
key.Hostname, escapeName(key.OrchestratorID), escapeName(key.WorkloadID))
endpoints := workload + "/endpoint"
return []string{endpoints, workload}, nil
}
func (key WorkloadEndpointKey) valueType() (reflect.Type, error) {
return reflect.TypeOf(WorkloadEndpoint{}), nil
}
func (key WorkloadEndpointKey) String() string {
return fmt.Sprintf("WorkloadEndpoint(node=%s, orchestrator=%s, workload=%s, name=%s)",
key.Hostname, key.OrchestratorID, key.WorkloadID, key.EndpointID)
}
type WorkloadEndpointListOptions struct {
Hostname string
OrchestratorID string
WorkloadID string
EndpointID string
}
func (options WorkloadEndpointListOptions) defaultPathRoot() string {
k := "/calico/v1/host"
if options.Hostname == "" {
return k
}
k = k + fmt.Sprintf("/%s/workload", options.Hostname)
if options.OrchestratorID == "" {
return k
}
k = k + fmt.Sprintf("/%s", escapeName(options.OrchestratorID))
if options.WorkloadID == "" {
return k
}
k = k + fmt.Sprintf("/%s/endpoint", escapeName(options.WorkloadID))
if options.EndpointID == "" {
return k
}
k = k + fmt.Sprintf("/%s", escapeName(options.EndpointID))
return k
}
func (options WorkloadEndpointListOptions) KeyFromDefaultPath(path string) Key {
log.Debugf("Get WorkloadEndpoint key from %s", path)
r := matchWorkloadEndpoint.FindAllStringSubmatch(path, -1)
if len(r) != 1 {
log.Debugf("Didn't match regex")
return nil
}
hostname := r[0][1]
orch := unescapeName(r[0][2])
workload := unescapeName(r[0][3])
endpointID := unescapeName(r[0][4])
if options.Hostname != "" && hostname != options.Hostname {
log.Debugf("Didn't match hostname %s != %s", options.Hostname, hostname)
return nil
}
if options.OrchestratorID != "" && orch != options.OrchestratorID {
log.Debugf("Didn't match orchestrator %s != %s", options.OrchestratorID, orch)
return nil
}
if options.WorkloadID != "" && workload != options.WorkloadID {
log.Debugf("Didn't match workload %s != %s", options.WorkloadID, workload)
return nil
}
if options.EndpointID != "" && endpointID != options.EndpointID {
log.Debugf("Didn't match endpoint ID %s != %s", options.EndpointID, endpointID)
return nil
}
return WorkloadEndpointKey{
Hostname: hostname,
OrchestratorID: orch,
WorkloadID: workload,
EndpointID: endpointID,
}
}
type WorkloadEndpoint struct {
State string `json:"state"`
Name string `json:"name"`
ActiveInstanceID string `json:"active_instance_id"`
Mac *net.MAC `json:"mac"`
ProfileIDs []string `json:"profile_ids"`
IPv4Nets []net.IPNet `json:"ipv4_nets"`
IPv6Nets []net.IPNet `json:"ipv6_nets"`
IPv4NAT []IPNAT `json:"ipv4_nat,omitempty"`
IPv6NAT []IPNAT `json:"ipv6_nat,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
IPv4Gateway *net.IP `json:"ipv4_gateway,omitempty" validate:"omitempty,ipv4"`
IPv6Gateway *net.IP `json:"ipv6_gateway,omitempty" validate:"omitempty,ipv6"`
Ports []EndpointPort `json:"ports,omitempty" validate:"dive"`
GenerateName string `json:"generate_name,omitempty"`
AllowSpoofedSourcePrefixes []net.IPNet `json:"allow_spoofed_source_ips,omitempty"`
Annotations map[string]string `json:"annotations,omitempty"`
}
type EndpointPort struct {
Name string `json:"name" validate:"name"`
Protocol numorstring.Protocol `json:"protocol"`
Port uint16 `json:"port" validate:"gt=0"`
}
// IPNat contains a single NAT mapping for a WorkloadEndpoint resource.
type IPNAT struct {
// The internal IP address which must be associated with the owning endpoint via the
// configured IPNetworks for the endpoint.
IntIP net.IP `json:"int_ip" validate:"ip"`
// The external IP address.
ExtIP net.IP `json:"ext_ip" validate:"ip"`
}

View File

@@ -0,0 +1,178 @@
// Copyright (c) 2016-2018 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"
"strings"
"regexp"
"reflect"
log "github.com/sirupsen/logrus"
"github.com/projectcalico/calico/libcalico-go/lib/errors"
)
var (
matchWorkloadEndpointStatus = regexp.MustCompile("^/?calico/felix/v2/([^/]+)/host/([^/]+)/workload/([^/]+)/([^/]+)/endpoint/([^/]+)$")
)
type WorkloadEndpointStatusKey struct {
Hostname string `json:"-"`
OrchestratorID string `json:"-"`
WorkloadID string `json:"-"`
EndpointID string `json:"-"`
RegionString string
}
func (key WorkloadEndpointStatusKey) defaultPath() (string, error) {
if key.Hostname == "" {
return "", errors.ErrorInsufficientIdentifiers{Name: "hostname"}
}
if key.OrchestratorID == "" {
return "", errors.ErrorInsufficientIdentifiers{Name: "orchestrator"}
}
if key.WorkloadID == "" {
return "", errors.ErrorInsufficientIdentifiers{Name: "workload"}
}
if key.EndpointID == "" {
return "", errors.ErrorInsufficientIdentifiers{Name: "endpoint"}
}
if key.RegionString == "" {
return "", errors.ErrorInsufficientIdentifiers{Name: "regionString"}
}
if strings.Contains(key.RegionString, "/") {
return "", ErrorSlashInRegionString(key.RegionString)
}
return fmt.Sprintf("/calico/felix/v2/%s/host/%s/workload/%s/%s/endpoint/%s",
key.RegionString,
key.Hostname, escapeName(key.OrchestratorID), escapeName(key.WorkloadID), escapeName(key.EndpointID)), nil
}
func (key WorkloadEndpointStatusKey) defaultDeletePath() (string, error) {
return key.defaultPath()
}
func (key WorkloadEndpointStatusKey) defaultDeleteParentPaths() ([]string, error) {
if key.Hostname == "" {
return nil, errors.ErrorInsufficientIdentifiers{Name: "hostname"}
}
if key.OrchestratorID == "" {
return nil, errors.ErrorInsufficientIdentifiers{Name: "orchestrator"}
}
if key.WorkloadID == "" {
return nil, errors.ErrorInsufficientIdentifiers{Name: "workload"}
}
if key.RegionString == "" {
return nil, errors.ErrorInsufficientIdentifiers{Name: "regionString"}
}
if strings.Contains(key.RegionString, "/") {
return nil, ErrorSlashInRegionString(key.RegionString)
}
workload := fmt.Sprintf("/calico/felix/v2/%s/host/%s/workload/%s/%s",
key.RegionString,
key.Hostname, escapeName(key.OrchestratorID), escapeName(key.WorkloadID))
endpoints := workload + "/endpoint"
return []string{endpoints, workload}, nil
}
func (key WorkloadEndpointStatusKey) valueType() (reflect.Type, error) {
return reflect.TypeOf(WorkloadEndpointStatus{}), nil
}
func (key WorkloadEndpointStatusKey) String() string {
return fmt.Sprintf("WorkloadEndpointStatus(hostname=%s, orchestrator=%s, workload=%s, name=%s)",
key.Hostname, key.OrchestratorID, key.WorkloadID, key.EndpointID)
}
type WorkloadEndpointStatusListOptions struct {
Hostname string
OrchestratorID string
WorkloadID string
EndpointID string
RegionString string
}
func (options WorkloadEndpointStatusListOptions) defaultPathRoot() string {
k := "/calico/felix/v2/"
if options.RegionString == "" {
return k
}
k = k + options.RegionString + "/host"
if options.Hostname == "" {
return k
}
k = k + fmt.Sprintf("/%s/workload", options.Hostname)
if options.OrchestratorID == "" {
return k
}
k = k + fmt.Sprintf("/%s", escapeName(options.OrchestratorID))
if options.WorkloadID == "" {
return k
}
k = k + fmt.Sprintf("/%s/endpoint", escapeName(options.WorkloadID))
if options.EndpointID == "" {
return k
}
k = k + fmt.Sprintf("/%s", escapeName(options.EndpointID))
return k
}
func (options WorkloadEndpointStatusListOptions) KeyFromDefaultPath(ekey string) Key {
log.Debugf("Get WorkloadEndpoint key from %s", ekey)
r := matchWorkloadEndpointStatus.FindAllStringSubmatch(ekey, -1)
if len(r) != 1 {
log.Debugf("Didn't match regex")
return nil
}
regionString := r[0][1]
hostname := r[0][2]
orchID := unescapeName(r[0][3])
workloadID := unescapeName(r[0][4])
endpointID := unescapeName(r[0][5])
if options.RegionString != "" && regionString != options.RegionString {
log.Debugf("Didn't match region %s != %s", options.RegionString, regionString)
return nil
}
if options.Hostname != "" && hostname != options.Hostname {
log.Debugf("Didn't match hostname %s != %s", options.Hostname, hostname)
return nil
}
if options.OrchestratorID != "" && orchID != options.OrchestratorID {
log.Debugf("Didn't match orchestrator %s != %s", options.OrchestratorID, orchID)
return nil
}
if options.WorkloadID != "" && workloadID != options.WorkloadID {
log.Debugf("Didn't match workload %s != %s", options.WorkloadID, workloadID)
return nil
}
if options.EndpointID != "" && endpointID != options.EndpointID {
log.Debugf("Didn't match endpoint ID %s != %s", options.EndpointID, endpointID)
return nil
}
return WorkloadEndpointStatusKey{
Hostname: hostname,
OrchestratorID: orchID,
WorkloadID: workloadID,
EndpointID: endpointID,
RegionString: regionString,
}
}
type WorkloadEndpointStatus struct {
Status string `json:"status"`
}