Skip to content

Commit da01220

Browse files
committed
init cilium res
1 parent aecea7b commit da01220

File tree

23 files changed

+1536
-26
lines changed

23 files changed

+1536
-26
lines changed

internal/alias.go

Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ type ResourceMetas map[types.GVR]metav1.APIResource
2525
type Aliases struct {
2626
aliases map[string]types.GVR
2727
metas ResourceMetas
28+
cilium bool
2829
}
2930

3031
// NewAliases returns a new instance.
@@ -49,25 +50,46 @@ func (a *Aliases) Dump() {
4950
}
5051
}
5152

52-
var customShortNames = map[string][]string{
53-
"cluster": {"cl"},
54-
"secrets": {"sec"},
55-
"deployments": {"dp"},
56-
"clusterroles": {"cr"},
57-
"clusterrolebindings": {"crb"},
58-
"roles": {"ro"},
59-
"rolebindings": {"rb"},
60-
"networkpolicies": {"np"},
61-
"httproutes": {"gwr"},
62-
"gatewayclassess": {"gwc"},
63-
"gateways": {"gw"},
53+
type ShortNames map[R][]string
54+
55+
var customShortNames = ShortNames{
56+
CL: {"cl"},
57+
SEC: {"sec"},
58+
DP: {"dp"},
59+
CR: {"cr"},
60+
CRB: {"crb"},
61+
RO: {"ro"},
62+
ROB: {"rb"},
63+
NP: {"np"},
64+
GWR: {"gwr"},
65+
GWC: {"gwc"},
66+
GW: {"gw"},
67+
}
68+
69+
func (a *Aliases) Inject(ss ShortNames) {
70+
for gvr, res := range a.metas {
71+
if kk, ok := ss[R(res.Name)]; ok {
72+
for _, k := range kk {
73+
a.aliases[k] = gvr
74+
}
75+
}
76+
}
77+
}
78+
79+
func (a *Aliases) IsNamespaced(gvr types.GVR) bool {
80+
if r, ok := a.metas[gvr]; ok {
81+
return r.Namespaced
82+
}
83+
84+
return true
6485
}
6586

6687
// Init loads the aliases glossary.
6788
func (a *Aliases) Init(c types.Connection) error {
68-
if err := a.loadPreferred(c); err != nil {
69-
return err
70-
}
89+
return a.loadPreferred(c)
90+
}
91+
92+
func (a *Aliases) Realize() {
7193
for gvr, res := range a.metas {
7294
a.aliases[res.Name] = gvr
7395
if res.SingularName != "" {
@@ -76,7 +98,7 @@ func (a *Aliases) Init(c types.Connection) error {
7698
for _, n := range res.ShortNames {
7799
a.aliases[n] = gvr
78100
}
79-
if kk, ok := customShortNames[res.Name]; ok {
101+
if kk, ok := customShortNames[R(res.Name)]; ok {
80102
for _, k := range kk {
81103
a.aliases[k] = gvr
82104
}
@@ -91,8 +113,6 @@ func (a *Aliases) Init(c types.Connection) error {
91113
}
92114
}
93115
}
94-
95-
return nil
96116
}
97117

98118
func greaterV(v1, v2 string) bool {
@@ -122,9 +142,14 @@ func (a *Aliases) TitleFor(s string, plural bool) string {
122142
if plural {
123143
return m.Name
124144
}
145+
125146
return m.SingularName
126147
}
127148

149+
func (a *Aliases) IsCiliumCluster() bool {
150+
return a.cilium
151+
}
152+
128153
func (a *Aliases) loadPreferred(c types.Connection) error {
129154
dial, err := c.CachedDiscovery()
130155
if err != nil {
@@ -141,6 +166,9 @@ func (a *Aliases) loadPreferred(c types.Connection) error {
141166
}
142167
for _, r := range l.APIResources {
143168
gvr := types.NewGVRFromAPIRes(gv, r)
169+
if !a.cilium && strings.Contains(gvr.G(), "cilium.io") {
170+
a.cilium = true
171+
}
144172
r.Group, r.Version = gvr.G(), gvr.V()
145173
if r.SingularName == "" {
146174
r.SingularName = strings.ToLower(r.Kind)

internal/cilium/cache/cep.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// Copyright Authors of Popeye
3+
4+
package cache
5+
6+
import (
7+
"fmt"
8+
"strconv"
9+
"sync"
10+
11+
v2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2"
12+
"github.com/derailed/popeye/internal"
13+
icache "github.com/derailed/popeye/internal/cache"
14+
"github.com/derailed/popeye/internal/cilium"
15+
"github.com/derailed/popeye/internal/db"
16+
)
17+
18+
const CIDKey = "cid"
19+
20+
// CiliumEndpoint represents a CiliumEndpoint cache.
21+
type CiliumEndpoint struct {
22+
db *db.DB
23+
}
24+
25+
// NewCiliumEndpoint returns a CiliumEndpoint cache.
26+
func NewCiliumEndpoint(dba *db.DB) *CiliumEndpoint {
27+
return &CiliumEndpoint{db: dba}
28+
}
29+
30+
// CiliumEndpointRefs computes all CiliumEndpoints external references.
31+
func (p *CiliumEndpoint) CEPRefs(refs *sync.Map) error {
32+
txn, it := p.db.MustITFor(internal.Glossary[cilium.CEP])
33+
defer txn.Abort()
34+
for o := it.Next(); o != nil; o = it.Next() {
35+
cep, ok := o.(*v2.CiliumEndpoint)
36+
if !ok {
37+
return fmt.Errorf("expected a CiliumEndpoint but got %T", o)
38+
}
39+
if cep.Status.Identity != nil {
40+
key := icache.ResFqn(CIDKey, icache.FQN("", strconv.Itoa(int(cep.Status.Identity.ID))))
41+
refs.Store(key, internal.AllKeys)
42+
}
43+
}
44+
45+
return nil
46+
}

internal/cilium/cilium.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// Copyright Authors of Popeye
3+
4+
package cilium
5+
6+
import (
7+
"github.com/derailed/popeye/internal"
8+
"github.com/derailed/popeye/types"
9+
)
10+
11+
func init() {
12+
for _, r := range CiliumRS {
13+
internal.Glossary[r] = types.BlankGVR
14+
}
15+
}
16+
17+
const (
18+
CEP internal.R = "ciliumendpoints"
19+
CID internal.R = "ciliumidentities"
20+
CNP internal.R = "ciliumnetworkpolicies"
21+
CCNP internal.R = "ciliumclusterwidenetworkpolicies"
22+
)
23+
24+
var CiliumRS = []internal.R{CEP, CID, CNP, CCNP}
25+
26+
var Aliases = internal.ShortNames{
27+
CEP: {"cep"},
28+
CID: {"cid"},
29+
}

internal/cilium/lint/ccnp.go

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// Copyright Authors of Popeye
3+
4+
package lint
5+
6+
import (
7+
"context"
8+
"fmt"
9+
10+
v2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2"
11+
"github.com/cilium/cilium/pkg/policy/api"
12+
"github.com/derailed/popeye/internal"
13+
"github.com/derailed/popeye/internal/cilium"
14+
"github.com/derailed/popeye/internal/client"
15+
"github.com/derailed/popeye/internal/db"
16+
"github.com/derailed/popeye/internal/issues"
17+
ilint "github.com/derailed/popeye/internal/lint"
18+
v1 "k8s.io/api/core/v1"
19+
)
20+
21+
// CiliumClusterwideNetworkPolicy tracks CiliumClusterwideNetworkPolicy sanitization.
22+
type CiliumClusterwideNetworkPolicy struct {
23+
*issues.Collector
24+
db *db.DB
25+
}
26+
27+
// NewCiliumClusterwideNetworkPolicy returns a new instance.
28+
func NewCiliumClusterwideNetworkPolicy(c *issues.Collector, db *db.DB) *CiliumClusterwideNetworkPolicy {
29+
return &CiliumClusterwideNetworkPolicy{
30+
Collector: c,
31+
db: db,
32+
}
33+
}
34+
35+
// Lint lints the resource.
36+
func (s *CiliumClusterwideNetworkPolicy) Lint(ctx context.Context) error {
37+
txn, it := s.db.MustITFor(internal.Glossary[cilium.CCNP])
38+
defer txn.Abort()
39+
for o := it.Next(); o != nil; o = it.Next() {
40+
ccnp := o.(*v2.CiliumClusterwideNetworkPolicy)
41+
fqn := client.FQN("", ccnp.Name)
42+
s.InitOutcome(fqn)
43+
ctx = internal.WithSpec(ctx, ilint.SpecFor(fqn, ccnp))
44+
45+
rules := ccnp.Specs
46+
if ccnp.Spec != nil {
47+
rules = append(rules, ccnp.Spec)
48+
}
49+
for _, r := range rules {
50+
if err := s.checkRule(ctx, r); err != nil {
51+
s.AddErr(ctx, err)
52+
}
53+
}
54+
}
55+
56+
return nil
57+
}
58+
59+
func (s *CiliumClusterwideNetworkPolicy) checkRule(ctx context.Context, r *api.Rule) error {
60+
if r.EndpointSelector.Size() > 0 {
61+
if ok, err := s.checkEPSel(r.EndpointSelector); err != nil {
62+
return err
63+
} else if !ok {
64+
s.AddCode(ctx, 1700, "endpoint")
65+
}
66+
}
67+
if r.NodeSelector.Size() > 0 {
68+
if ok, err := s.checkNodeSel(r.NodeSelector); err != nil {
69+
return err
70+
} else if !ok {
71+
s.AddCode(ctx, 1701)
72+
}
73+
}
74+
for _, ing := range r.Ingress {
75+
for _, sel := range ing.FromEndpoints {
76+
if ok, err := s.checkEPSel(sel); err != nil {
77+
return err
78+
} else if !ok {
79+
s.AddCode(ctx, 1700, "ingress")
80+
}
81+
}
82+
}
83+
for _, eg := range r.Egress {
84+
for _, sel := range eg.ToEndpoints {
85+
if ok, err := s.checkEPSel(sel); err != nil {
86+
return err
87+
} else if !ok {
88+
s.AddCode(ctx, 1700, "egress")
89+
}
90+
}
91+
}
92+
93+
return nil
94+
}
95+
96+
func (s *CiliumClusterwideNetworkPolicy) checkEPSel(sel api.EndpointSelector) (bool, error) {
97+
mm, err := s.matchCEPsBySel(sel)
98+
if err != nil {
99+
return false, err
100+
}
101+
102+
return len(mm) > 0, nil
103+
}
104+
105+
func (s *CiliumClusterwideNetworkPolicy) checkNodeSel(sel api.EndpointSelector) (bool, error) {
106+
mm, err := s.matchNodesBySel(sel)
107+
if err != nil {
108+
return false, err
109+
}
110+
111+
return len(mm) > 0, nil
112+
}
113+
114+
func (s *CiliumClusterwideNetworkPolicy) matchNodesBySel(sel api.EndpointSelector) ([]string, error) {
115+
txn := s.db.Txn(false)
116+
defer txn.Abort()
117+
txn, it := s.db.MustITFor(internal.Glossary[internal.NO])
118+
defer txn.Abort()
119+
mm := make([]string, 0, 10)
120+
for o := it.Next(); o != nil; o = it.Next() {
121+
no, ok := o.(*v1.Node)
122+
if !ok {
123+
return nil, fmt.Errorf("expecting node but got %s", o)
124+
}
125+
fqn := client.FQN("", no.Name)
126+
if matchSelector(no.Labels, sel) {
127+
mm = append(mm, fqn)
128+
}
129+
}
130+
131+
return mm, nil
132+
}
133+
134+
func (s *CiliumClusterwideNetworkPolicy) matchCEPsBySel(sel api.EndpointSelector) ([]string, error) {
135+
txn := s.db.Txn(false)
136+
defer txn.Abort()
137+
txn, it := s.db.MustITFor(internal.Glossary[cilium.CEP])
138+
defer txn.Abort()
139+
mm := make([]string, 0, 10)
140+
for o := it.Next(); o != nil; o = it.Next() {
141+
cep, ok := o.(*v2.CiliumEndpoint)
142+
if !ok {
143+
return nil, fmt.Errorf("expecting cilium endpoint but got %s", o)
144+
}
145+
fqn := client.FQN(cep.Namespace, cep.Name)
146+
if matchSelector(cep.Labels, sel) {
147+
mm = append(mm, fqn)
148+
}
149+
}
150+
151+
return mm, nil
152+
}

internal/cilium/lint/ccnp_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// Copyright Authors of Popeye
3+
4+
package lint
5+
6+
import (
7+
"testing"
8+
9+
v2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2"
10+
"github.com/derailed/popeye/internal"
11+
"github.com/derailed/popeye/internal/cilium"
12+
"github.com/derailed/popeye/internal/db"
13+
"github.com/derailed/popeye/internal/rules"
14+
"github.com/derailed/popeye/internal/test"
15+
"github.com/stretchr/testify/assert"
16+
)
17+
18+
func TestCiliumClusterwideNetworkPolicy(t *testing.T) {
19+
dba, err := test.NewTestDB()
20+
assert.NoError(t, err)
21+
l := db.NewLoader(dba)
22+
23+
ctx := test.MakeCtx(t)
24+
assert.NoError(t, test.LoadDB[*v2.CiliumClusterwideNetworkPolicy](ctx, l.DB, "ccnp/1.yaml", internal.Glossary[cilium.CCNP]))
25+
assert.NoError(t, test.LoadDB[*v2.CiliumEndpoint](ctx, l.DB, "cep/1.yaml", internal.Glossary[cilium.CEP]))
26+
27+
li := NewCiliumClusterwideNetworkPolicy(test.MakeCollector(t), dba)
28+
assert.Nil(t, li.Lint(test.MakeContext("cilium.io/v2/ciliumclusterwidenetworkpolicies", "ciliumclusterwidenetworkpolicies")))
29+
assert.Equal(t, 3, len(li.Outcome()))
30+
31+
ii := li.Outcome()["ccnp1"]
32+
assert.Equal(t, 0, len(ii))
33+
34+
ii = li.Outcome()["ccnp2"]
35+
assert.Equal(t, 3, len(ii))
36+
assert.Equal(t, `[POP-1700] No cilium endpoints matched endpoint selector`, ii[0].Message)
37+
assert.Equal(t, rules.ErrorLevel, ii[0].Level)
38+
assert.Equal(t, `[POP-1700] No cilium endpoints matched ingress selector`, ii[1].Message)
39+
assert.Equal(t, rules.ErrorLevel, ii[1].Level)
40+
assert.Equal(t, `[POP-1700] No cilium endpoints matched egress selector`, ii[2].Message)
41+
assert.Equal(t, rules.ErrorLevel, ii[2].Level)
42+
43+
ii = li.Outcome()["ccnp3"]
44+
assert.Equal(t, 1, len(ii))
45+
assert.Equal(t, `[POP-1701] No nodes matched node selector`, ii[0].Message)
46+
assert.Equal(t, rules.ErrorLevel, ii[0].Level)
47+
}

0 commit comments

Comments
 (0)