blob: 0a6e40181964a9b2a43d3ed0852a2db3e2107746 [file] [log] [blame]
/*
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 types
import (
"encoding/json"
"sync"
"ingress-ats/util"
set "github.com/deckarep/golang-set"
)
//----------------- nsSvc ---------------------------------
// map[namespace]map[svc][]PathPtr
type nsSvc struct {
// ns -> []svc -> []*path
NsToSvc map[string]*svcPaths
mux sync.RWMutex
}
// newNsSvc creates a new empty nsSvc
func newNsSvc() *nsSvc {
return &nsSvc{
NsToSvc: make(map[string]*svcPaths),
}
}
// SetNamespaceSvcPaths adds or sets a path/svc within namespace
func (n *nsSvc) SetNamespaceSvcPaths(namespace string, pathPtr *Path) {
n.mux.Lock()
if svcPathsPtr, found := n.NsToSvc[namespace]; found {
svcPathsPtr.AddSvcPath(pathPtr)
} else {
newSvcPaths := newSvcPaths()
newSvcPaths.AddSvcPath(pathPtr)
n.NsToSvc[namespace] = newSvcPaths
}
n.mux.Unlock()
}
// DelNamespaceSvcPath deletes a Path ptr from namespace
func (n *nsSvc) DelNamespaceSvcPath(pathPtr *Path) {
n.mux.Lock()
defer n.mux.Unlock()
namespace := pathPtr.GetNamespace()
if svcPathsPtr, found := n.NsToSvc[namespace]; found {
deletedSvc := svcPathsPtr.DelSvcPath(pathPtr)
if deletedSvc && svcPathsPtr.NumSvc() == 0 {
// when ns has no ingress defined deployed service
delete(n.NsToSvc, namespace)
}
}
}
// NumNamespace returns the number of namespaces being managed
func (n *nsSvc) NumNamespace() int {
n.mux.RLock()
defer n.mux.RUnlock()
return len(n.NsToSvc)
}
// HasSvc returns true if namespace exists, and svc exists in
// same namespace
func (n *nsSvc) HasSvc(namespace, svcName string) bool {
n.mux.RLock()
defer n.mux.RUnlock()
if svcPaths, found := n.NsToSvc[namespace]; found {
return svcPaths.Has(svcName)
}
return false
}
// NumHostPathInNamespace returns number of paths of hostName are in namespace
// TODO: can add more metadata in the future to make this faster
func (n *nsSvc) NoHostPathInNamespace(hostName, pathName, namespace string) bool {
res := 0
n.mux.RLock()
svcPathsPtr := n.NsToSvc[namespace]
n.mux.RUnlock()
svcPathsPtr.rLock()
for _, pathSet := range svcPathsPtr.StoPs {
for path := range pathSet.Iter() {
pathPtr := path.(*Path)
if pathPtr.GetHostName() == hostName &&
pathPtr.GetPathName() == pathName {
res++
}
}
}
svcPathsPtr.rUnlock()
return res == 0
}
// Iter returns a rangable channel of all Paths using svc, within namespace
func (n *nsSvc) Iter(namespace, svc string) <-chan interface{} {
n.mux.RLock()
defer n.mux.RUnlock()
return n.NsToSvc[namespace].Iter(svc)
}
func (n *nsSvc) String() string {
n.mux.RLock()
defer n.mux.RUnlock()
marshalled, _ := json.Marshal(n)
return util.FmtMarshalled(marshalled)
}
//----------------- svcPaths ------------------------------
// serviceName -> []PathPtr
type svcPaths struct {
StoPs map[string]set.Set `json:"StoPs"` // svcName -> []Pathptr
mux sync.RWMutex
}
// newSvcPaths returns a new empty SvcPaths struct ptr
func newSvcPaths() *svcPaths {
return &svcPaths{StoPs: make(map[string]set.Set)}
}
func (s *svcPaths) rLock() {
s.mux.RLock()
}
func (s *svcPaths) rUnlock() {
s.mux.RUnlock()
}
// checkAddSvc adds/initialize svc if not stored
func (s *svcPaths) checkAddSvc(svc string) {
s.mux.Lock()
if _, found := s.StoPs[svc]; !found {
s.StoPs[svc] = set.NewSet()
}
s.mux.Unlock()
}
// AddSvcPath adds a Mapping svc --> PathPtr safely
func (s *svcPaths) AddSvcPath(pathPtr *Path) {
// path cannot be updated during this operation
pathPtr.RLock()
s.checkAddSvc(pathPtr.ServiceName)
// v ^ order here cannot switch!
s.mux.RLock()
s.StoPs[pathPtr.ServiceName].Add(pathPtr)
s.mux.RUnlock()
pathPtr.RUnlock()
}
// DelSvcPath deletes a path ptr associated with svc
// returns true if svc no longer referenced by any path/backend and is deleted
func (s *svcPaths) DelSvcPath(pathPtr *Path) (deletedSvc bool) {
s.mux.Lock()
pathPtr.RLock() // path cannot be updated during this operation
defer pathPtr.RUnlock()
defer s.mux.Unlock()
svc := pathPtr.ServiceName
if _, found := s.StoPs[svc]; found {
s.StoPs[svc].Remove(pathPtr)
// if svc no longer needed by any backend
if s.StoPs[svc].Cardinality() == 0 {
delete(s.StoPs, svc)
return true
}
}
return false
}
// Iter returns a channel of path ptrs associated with svc
// this is meant to be used with "range"
func (s *svcPaths) Iter(svc string) <-chan interface{} {
s.mux.RLock()
defer s.mux.RUnlock()
return s.StoPs[svc].Iter() // this itself is threadsafe
}
// Has returns whether the svc is stored
func (s *svcPaths) Has(svc string) bool {
s.mux.RLock()
defer s.mux.RUnlock()
_, found := s.StoPs[svc]
return found
}
// NumSvc returns number of svcs stored
func (s *svcPaths) NumSvc() int {
s.mux.RLock()
defer s.mux.RUnlock()
return len(s.StoPs)
}
func (s *svcPaths) String() string {
s.mux.RLock()
defer s.mux.RUnlock()
marshalled, _ := json.Marshal(s)
return util.FmtMarshalled(marshalled)
}
//----------------- hostNamespaces ------------------------
// hostName -> []namespace
type hostNamespaces struct {
HtoN map[string]set.Set
mux sync.RWMutex
}
// newHostNamespaces returns a new empty hostNamespaces struct
func newHostNamespaces() *hostNamespaces {
return &hostNamespaces{
HtoN: make(map[string]set.Set),
}
}
// checkInit checks of hostName is initialized in map
func (h *hostNamespaces) checkInit(hostName string) {
h.mux.Lock()
if _, found := h.HtoN[hostName]; !found {
h.HtoN[hostName] = set.NewSet()
}
h.mux.Unlock()
}
// AddNamespace adds namespace to hostname's set of namespaces
// that it belongs to
func (h *hostNamespaces) AddNamespace(hostName, namespace string) {
h.checkInit(hostName)
h.mux.RLock()
h.HtoN[hostName].Add(namespace)
h.mux.RUnlock()
}
// DelNamespace deletes namespace from hostname's set of namespaces
// if host no longer belongs in any namespace, function will clear hostname
// from its map and return true.
func (h *hostNamespaces) DelNamespace(hostName, namespace string) (deletedHost bool) {
h.mux.Lock()
defer h.mux.Unlock()
_, found := h.HtoN[hostName]
if found {
h.HtoN[hostName].Remove(namespace)
if h.HtoN[hostName].Cardinality() == 0 {
delete(h.HtoN, hostName)
return true
}
}
return false
}
// hostPathInNamespace returns true if host has a path in namespace
func (h *hostNamespaces) hostPathInNamespace(hostName, namespace string) bool {
h.mux.RLock()
defer h.mux.RUnlock()
_, found := h.HtoN[hostName]
if found {
return h.HtoN[hostName].Contains(namespace)
}
return false
}
// hostOnlyInNamespace returns true if host only has path(s) in namespace
// and nowhere else
func (h *hostNamespaces) hostOnlyInNamespace(hostName, namespace string) bool {
h.mux.RLock()
defer h.mux.RUnlock()
_, found := h.HtoN[hostName]
if !found {
// returning false here because host might
// very well be deleted by another thread before this function
// got ran; in which case, return false so caller don't do
// anything that might be dangerous
return false
}
return h.HtoN[hostName].Cardinality() == 1 &&
h.HtoN[hostName].Contains(namespace)
}