Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions pkg/render/apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -1452,6 +1452,8 @@ func (c *apiServerComponent) tigeraAPIServerClusterRole() *rbacv1.ClusterRole {
"globalreporttypes",
"globalthreatfeeds",
"globalthreatfeeds/status",
"hostqospolicies",
"hostqospolicies/status",
"licensekeys",
"managedclusters",
"managedclusters/status",
Expand Down
22 changes: 22 additions & 0 deletions pkg/render/apiserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,28 @@ var _ = Describe("API server rendering tests (Calico Enterprise)", func() {
Expect(clusterRole.Rules).To(ConsistOf(append(networkAdminPolicyRules, rbacManagementNetworkAdminRules...)))
})

It("should grant the calico-apiserver access to hostqospolicies backing storage", func() {
component, err := render.APIServer(cfg)
Expect(err).NotTo(HaveOccurred())
resources, _ := component.Objects()

clusterRole := rtest.GetResource(resources, "calico-apiserver", "", "rbac.authorization.k8s.io", "v1", "ClusterRole").(*rbacv1.ClusterRole)

var backingRule *rbacv1.PolicyRule
for i := range clusterRole.Rules {
r := &clusterRole.Rules[i]
if len(r.APIGroups) == 2 && r.APIGroups[0] == "projectcalico.org" && r.APIGroups[1] == "crd.projectcalico.org" {
for _, res := range r.Resources {
if res == "alertexceptions" {
backingRule = r
}
}
}
}
Expect(backingRule).NotTo(BeNil(), "expected the Enterprise backing-storage rule")
Expect(backingRule.Resources).To(ContainElements("hostqospolicies", "hostqospolicies/status"))
})

It("should render resources without an aggregation server", func() {
cfg.RequiresAggregationServer = false

Expand Down
8 changes: 8 additions & 0 deletions pkg/render/kubecontrollers/kube-controllers.go
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,14 @@ func kubeControllersRoleEnterpriseCommonRules(cfg *KubeControllersConfiguration)
Resources: []string{"packetcaptures/status"},
Verbs: []string{"update"},
},
{
// The HostQoSPolicy status aggregator watches status written per-node by
// each Felix and rolls it up into the Ready condition via the /status
// subresource. Mirrors the ippools/status pattern.
APIGroups: []string{"projectcalico.org", "crd.projectcalico.org"},
Resources: []string{"hostqospolicies", "hostqospolicies/status"},
Verbs: []string{"get", "list", "watch", "update"},
},
}

if cfg.WAFGatewayExtensionEnabled {
Expand Down
13 changes: 10 additions & 3 deletions pkg/render/kubecontrollers/kube-controllers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,14 @@ var _ = Describe("kube-controllers rendering tests", func() {
Expect(len(dp.Spec.Template.Spec.Volumes)).To(Equal(1))

clusterRole := rtest.GetResource(resources, kubecontrollers.KubeControllerRole, "", "rbac.authorization.k8s.io", "v1", "ClusterRole").(*rbacv1.ClusterRole)
Expect(clusterRole.Rules).To(HaveLen(38), "cluster role should have 38 rules")
Expect(clusterRole.Rules).To(HaveLen(39), "cluster role should have 39 rules")

// HostQoSPolicy status aggregator RBAC (Enterprise only).
Expect(clusterRole.Rules).To(ContainElement(rbacv1.PolicyRule{
APIGroups: []string{"projectcalico.org", "crd.projectcalico.org"},
Resources: []string{"hostqospolicies", "hostqospolicies/status"},
Verbs: []string{"get", "list", "watch", "update"},
}))

// Application-layer reconciler RBAC: WAF CRDs (resources, /status, /finalizers).
Expect(clusterRole.Rules).To(ContainElement(rbacv1.PolicyRule{
Expand Down Expand Up @@ -533,7 +540,7 @@ var _ = Describe("kube-controllers rendering tests", func() {
Expect(dp.Spec.Template.Spec.Volumes[0].ConfigMap.Name).To(Equal("tigera-ca-bundle"))

clusterRole := rtest.GetResource(resources, kubecontrollers.EsKubeControllerRole, "", "rbac.authorization.k8s.io", "v1", "ClusterRole").(*rbacv1.ClusterRole)
Expect(clusterRole.Rules).To(HaveLen(36), "cluster role should have 36 rules")
Expect(clusterRole.Rules).To(HaveLen(37), "cluster role should have 37 rules")
Expect(clusterRole.Rules).To(ContainElement(
rbacv1.PolicyRule{
APIGroups: []string{""},
Expand Down Expand Up @@ -794,7 +801,7 @@ var _ = Describe("kube-controllers rendering tests", func() {
Expect(dp.Spec.Template.Spec.Containers[0].Image).To(Equal("test-reg/tigera/calico:" + components.ComponentTigeraCalico.Version))

clusterRole := rtest.GetResource(resources, kubecontrollers.EsKubeControllerRole, "", "rbac.authorization.k8s.io", "v1", "ClusterRole").(*rbacv1.ClusterRole)
Expect(clusterRole.Rules).To(HaveLen(36), "cluster role should have 36 rules")
Expect(clusterRole.Rules).To(HaveLen(37), "cluster role should have 37 rules")
Expect(clusterRole.Rules).To(ContainElement(
rbacv1.PolicyRule{
APIGroups: []string{""},
Expand Down
10 changes: 9 additions & 1 deletion pkg/render/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,6 @@ func (c *nodeComponent) nodeRole() *rbacv1.ClusterRole {
"globalnetworkpolicies",
"globalnetworksets",
"hostendpoints",
"hostqospolicies",
"ipamblocks",
"ippools",
"ipreservations",
Expand Down Expand Up @@ -576,6 +575,7 @@ func (c *nodeComponent) nodeRole() *rbacv1.ClusterRole {
"bfdconfigurations",
"egressgatewaypolicies",
"externalnetworks",
"hostqospolicies",
"licensekeys",
"networks",
"packetcaptures",
Expand All @@ -592,6 +592,14 @@ func (c *nodeComponent) nodeRole() *rbacv1.ClusterRole {
},
Verbs: []string{"update"},
},
{
// Felix writes its own per-node entry in HostQoSPolicy status.nodes[]
// via Server-Side Apply (FieldManager: felix-<node-name>). SSA is an
// HTTP PATCH, so patch is required rather than update.
APIGroups: []string{"projectcalico.org", "crd.projectcalico.org"},
Resources: []string{"hostqospolicies/status"},
Verbs: []string{"patch"},
},
}
role.Rules = append(role.Rules, extraRules...)
}
Expand Down
38 changes: 38 additions & 0 deletions pkg/render/node_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -741,6 +741,44 @@ var _ = Describe("Node rendering tests", func() {
verifyProbesAndLifecycle(ds, false, true)
})

It("should grant calico-node HostQoSPolicy RBAC only for Enterprise", func() {
// Enterprise: read access plus patch on the status subresource, which
// Felix writes via Server-Side Apply.
defaultInstance.Variant = operatorv1.CalicoEnterprise
component := render.Node(&cfg)
Expect(component.ResolveImages(nil)).To(BeNil())
resources, _ := component.Objects()
role := rtest.GetResource(resources, "calico-node", "", "rbac.authorization.k8s.io", "v1", "ClusterRole").(*rbacv1.ClusterRole)

Expect(role.Rules).To(ContainElement(rbacv1.PolicyRule{
APIGroups: []string{"projectcalico.org", "crd.projectcalico.org"},
Resources: []string{"hostqospolicies/status"},
Verbs: []string{"patch"},
}))
var readsHostQoS bool
for _, rule := range role.Rules {
for _, res := range rule.Resources {
if res == "hostqospolicies" {
readsHostQoS = true
}
}
}
Expect(readsHostQoS).To(BeTrue(), "Enterprise calico-node should read hostqospolicies")

// Calico (OSS): the CRD does not exist, so neither grant is rendered.
defaultInstance.Variant = operatorv1.Calico
component = render.Node(&cfg)
Expect(component.ResolveImages(nil)).To(BeNil())
resources, _ = component.Objects()
role = rtest.GetResource(resources, "calico-node", "", "rbac.authorization.k8s.io", "v1", "ClusterRole").(*rbacv1.ClusterRole)
for _, rule := range role.Rules {
for _, res := range rule.Resources {
Expect(res).NotTo(Equal("hostqospolicies"))
Expect(res).NotTo(Equal("hostqospolicies/status"))
}
}
})

It("should render felix service metric with FelixPrometheusMetricPort when FelixPrometheusMetricsEnabled is true", func() {
defaultInstance.Variant = operatorv1.CalicoEnterprise
cfg.NodeReporterMetricsPort = 9081
Expand Down
9 changes: 9 additions & 0 deletions pkg/render/nonclusterhost/nonclusterhost.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ func (c *nonClusterHostComponent) clusterRole() *rbacv1.ClusterRole {
"globalnetworkpolicies",
"globalnetworksets",
"hostendpoints",
"hostqospolicies",
"ipamblocks",
"ippools",
"licensekeys",
Expand All @@ -175,6 +176,14 @@ func (c *nonClusterHostComponent) clusterRole() *rbacv1.ClusterRole {
},
Verbs: []string{"get", "list", "watch"},
},
{
// Felix on a non-cluster host writes its own per-node entry in
// HostQoSPolicy status.nodes[] via Server-Side Apply, which is an
// HTTP PATCH.
APIGroups: []string{"projectcalico.org", "crd.projectcalico.org"},
Resources: []string{"hostqospolicies/status"},
Verbs: []string{"patch"},
},
}

// Calico fluent-bit rules
Expand Down
6 changes: 6 additions & 0 deletions pkg/render/nonclusterhost/nonclusterhost_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ var _ = Describe("NonClusterHost rendering tests", func() {
"globalnetworkpolicies",
"globalnetworksets",
"hostendpoints",
"hostqospolicies",
"ipamblocks",
"ippools",
"licensekeys",
Expand All @@ -137,6 +138,11 @@ var _ = Describe("NonClusterHost rendering tests", func() {
},
Verbs: []string{"get", "list", "watch"},
},
rbacv1.PolicyRule{
APIGroups: []string{"projectcalico.org", "crd.projectcalico.org"},
Resources: []string{"hostqospolicies/status"},
Verbs: []string{"patch"},
},
rbacv1.PolicyRule{
APIGroups: []string{""},
Resources: []string{"serviceaccounts/token"},
Expand Down
2 changes: 1 addition & 1 deletion pkg/render/typha.go
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,6 @@ func (c *typhaComponent) typhaRole() *rbacv1.ClusterRole {
"stagednetworkpolicies",
"globalnetworksets",
"hostendpoints",
"hostqospolicies",
"ipamblocks",
"ippools",
"ipreservations",
Expand Down Expand Up @@ -366,6 +365,7 @@ func (c *typhaComponent) typhaRole() *rbacv1.ClusterRole {
"deeppacketinspections",
"egressgatewaypolicies",
"externalnetworks",
"hostqospolicies",
"licensekeys",
"networks",
"packetcaptures",
Expand Down
34 changes: 34 additions & 0 deletions pkg/render/typha_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,40 @@ var _ = Describe("Typha rendering tests", func() {
}))
})

It("should grant calico-typha read on hostqospolicies only for Enterprise", func() {
hostQoSInRules := func(role *rbacv1.ClusterRole) bool {
for _, rule := range role.Rules {
for _, res := range rule.Resources {
if res == "hostqospolicies" {
return true
}
}
}
return false
}

cfg.Installation.Variant = operatorv1.CalicoEnterprise
component := render.Typha(&cfg)
Expect(component.ResolveImages(nil)).To(BeNil())
resources, _ := component.Objects()
role := rtest.GetResource(resources, "calico-typha", "", "rbac.authorization.k8s.io", "v1", "ClusterRole").(*rbacv1.ClusterRole)
Expect(hostQoSInRules(role)).To(BeTrue(), "Enterprise calico-typha should read hostqospolicies")

// Typha only relays the syncer, so it never writes status.
Expect(role.Rules).NotTo(ContainElement(rbacv1.PolicyRule{
APIGroups: []string{"projectcalico.org", "crd.projectcalico.org"},
Resources: []string{"hostqospolicies/status"},
Verbs: []string{"patch"},
}))

cfg.Installation.Variant = operatorv1.Calico
component = render.Typha(&cfg)
Expect(component.ResolveImages(nil)).To(BeNil())
resources, _ = component.Objects()
role = rtest.GetResource(resources, "calico-typha", "", "rbac.authorization.k8s.io", "v1", "ClusterRole").(*rbacv1.ClusterRole)
Expect(hostQoSInRules(role)).To(BeFalse(), "Calico OSS calico-typha should not reference hostqospolicies")
})

It("should render all resources for a default configuration", func() {
expectedResources := []struct {
name string
Expand Down
Loading