Skip to content

Commit

Permalink
add dependencies override and ability to rm rbacs by component (#527)
Browse files Browse the repository at this point in the history
* add dependencies override and ability to rm rbacs by component
  • Loading branch information
celenechang committed Jul 1, 2022
1 parent 0529b95 commit 0c9f570
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 17 deletions.
12 changes: 10 additions & 2 deletions controllers/datadogagent/controller_reconcile_v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
datadoghqv2alpha1 "github.com/DataDog/datadog-operator/apis/datadoghq/v2alpha1"
"github.com/DataDog/datadog-operator/controllers/datadogagent/dependencies"
"github.com/DataDog/datadog-operator/controllers/datadogagent/feature"
"github.com/DataDog/datadog-operator/controllers/datadogagent/override"
"github.com/DataDog/datadog-operator/pkg/controller/utils"
)

Expand Down Expand Up @@ -97,14 +98,21 @@ func (r *Reconciler) reconcileInstanceV2(ctx context.Context, logger logr.Logger
Scheme: r.scheme,
}
depsStore := dependencies.NewStore(instance, storeOptions)
resourcesManager := feature.NewResourceManagers(depsStore)
resourceManagers := feature.NewResourceManagers(depsStore)

var errs []error

// Set up dependencies required by enabled features
for id, feat := range features {
logger.Info("Dependency ManageDependencies", "featureID", id)
if featErr := feat.ManageDependencies(resourcesManager, requiredComponents); err != nil {
if featErr := feat.ManageDependencies(resourceManagers, requiredComponents); err != nil {
errs = append(errs, featErr)
}
}

// Examine user configuration to override any external dependencies (e.g. RBACs)
errs = append(errs, override.Dependencies(resourceManagers, instance.Spec.Override, instance.Namespace)...)

// Now create/update dependencies
errs = append(errs, depsStore.Apply(ctx, r.client)...)
if len(errs) > 0 {
Expand Down
17 changes: 17 additions & 0 deletions controllers/datadogagent/dependencies/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ type StoreClient interface {
AddOrUpdate(kind kubernetes.ObjectKind, obj client.Object) error
Get(kind kubernetes.ObjectKind, namespace, name string) (client.Object, bool)
GetOrCreate(kind kubernetes.ObjectKind, namespace, name string) (client.Object, bool)
Delete(kind kubernetes.ObjectKind, namespace string, name string) bool
}

// NewStore returns a new Store instance
Expand Down Expand Up @@ -166,6 +167,22 @@ func (ds *Store) GetOrCreate(kind kubernetes.ObjectKind, namespace, name string)
return obj, found
}

// Delete deletes an item from the store by kind, namespace and name.
func (ds *Store) Delete(kind kubernetes.ObjectKind, namespace string, name string) bool {
ds.mutex.RLock()
defer ds.mutex.RUnlock()

if _, found := ds.deps[kind]; !found {
return false
}
id := buildID(namespace, name)
if _, found := ds.deps[kind][id]; found {
delete(ds.deps[kind], id)
return true
}
return false
}

// Apply use to create/update resources in the api-server
func (ds *Store) Apply(ctx context.Context, k8sClient client.Client) []error {
ds.mutex.RLock()
Expand Down
28 changes: 14 additions & 14 deletions controllers/datadogagent/feature/enabledefault/feature.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ type defaultFeature struct {
namespace string
owner metav1.Object

credentialsInfo credentialsInfo
dcaTokenInfo dcaTokenInfo
clusterAgent clusterAgentConfig
agent agentConfig
clusterCheckRunner clusterCheckRunnerConfig
credentialsInfo credentialsInfo
dcaTokenInfo dcaTokenInfo
clusterAgent clusterAgentConfig
agent agentConfig
clusterChecksRunner clusterChecksRunnerConfig
}

type credentialsInfo struct {
Expand Down Expand Up @@ -84,7 +84,7 @@ type agentConfig struct {
serviceAccountName string
}

type clusterCheckRunnerConfig struct {
type clusterChecksRunnerConfig struct {
serviceAccountName string
}

Expand All @@ -95,7 +95,7 @@ func (f *defaultFeature) Configure(dda *v2alpha1.DatadogAgent) feature.RequiredC

f.clusterAgent.serviceAccountName = v2alpha1.GetClusterAgentServiceAccount(dda)
f.agent.serviceAccountName = v2alpha1.GetAgentServiceAccount(dda)
f.clusterCheckRunner.serviceAccountName = v2alpha1.GetClusterChecksRunnerServiceAccount(dda)
f.clusterChecksRunner.serviceAccountName = v2alpha1.GetClusterChecksRunnerServiceAccount(dda)

if dda.Spec.Global != nil {
if dda.Spec.Global.Credentials != nil {
Expand Down Expand Up @@ -162,7 +162,7 @@ func (f *defaultFeature) ConfigureV1(dda *v1alpha1.DatadogAgent) feature.Require
f.clusterAgent.serviceAccountName = v1alpha1.GetClusterAgentServiceAccount(dda)
f.agent.serviceAccountName = v1alpha1.GetAgentServiceAccount(dda)
f.clusterCheckRunner.serviceAccountName = v1alpha1.GetClusterChecksRunnerServiceAccount(dda)
f.clusterChecksRunner.serviceAccountName = v1alpha1.GetClusterChecksRunnerServiceAccount(dda)
// get info about credential
// If API key, app key _and_ token don't need a new secret, then don't create one.
Expand Down Expand Up @@ -255,7 +255,7 @@ func (f *defaultFeature) agentDependencies(managers feature.ResourceManagers, co
var errs []error
// serviceAccount
if f.agent.serviceAccountName != "" {
if err := managers.RBACManager().AddServiceAccount(f.namespace, f.agent.serviceAccountName); err != nil {
if err := managers.RBACManager().AddServiceAccountByComponent(f.namespace, f.agent.serviceAccountName, string(v2alpha1.NodeAgentComponentName)); err != nil {
errs = append(errs, err)
}
}
Expand All @@ -274,17 +274,17 @@ func (f *defaultFeature) clusterAgentDependencies(managers feature.ResourceManag
// serviceAccount
if f.clusterAgent.serviceAccountName != "" {
// Service Account creation
if err := managers.RBACManager().AddServiceAccount(f.namespace, f.clusterAgent.serviceAccountName); err != nil {
if err := managers.RBACManager().AddServiceAccountByComponent(f.namespace, f.clusterAgent.serviceAccountName, string(v2alpha1.ClusterAgentComponentName)); err != nil {
errs = append(errs, err)
}

// Role Creation
if err := managers.RBACManager().AddPolicyRules(f.namespace, componentdca.GetClusterAgentRbacResourcesName(f.owner), f.clusterAgent.serviceAccountName, componentdca.GetDefaultClusterAgentRolePolicyRules(f.owner)); err != nil {
if err := managers.RBACManager().AddPolicyRulesByComponent(f.namespace, componentdca.GetClusterAgentRbacResourcesName(f.owner), f.clusterAgent.serviceAccountName, componentdca.GetDefaultClusterAgentRolePolicyRules(f.owner), string(v2alpha1.ClusterAgentComponentName)); err != nil {
errs = append(errs, err)
}

// ClusterRole creation
if err := managers.RBACManager().AddClusterPolicyRules(f.namespace, componentdca.GetClusterAgentRbacResourcesName(f.owner), f.clusterAgent.serviceAccountName, componentdca.GetDefaultClusterAgentClusterRolePolicyRules(f.owner)); err != nil {
if err := managers.RBACManager().AddClusterPolicyRulesByComponent(f.namespace, componentdca.GetClusterAgentRbacResourcesName(f.owner), f.clusterAgent.serviceAccountName, componentdca.GetDefaultClusterAgentClusterRolePolicyRules(f.owner), string(v2alpha1.ClusterAgentComponentName)); err != nil {
errs = append(errs, err)
}
}
Expand All @@ -301,8 +301,8 @@ func (f *defaultFeature) clusterChecksRunnerDependencies(managers feature.Resour
_ = component
var errs []error
// serviceAccount
if f.clusterCheckRunner.serviceAccountName != "" {
if err := managers.RBACManager().AddServiceAccount(f.namespace, f.clusterCheckRunner.serviceAccountName); err != nil {
if f.clusterChecksRunner.serviceAccountName != "" {
if err := managers.RBACManager().AddServiceAccountByComponent(f.namespace, f.clusterChecksRunner.serviceAccountName, string(v2alpha1.ClusterChecksRunnerComponentName)); err != nil {
errs = append(errs, err)
}
}
Expand Down
93 changes: 92 additions & 1 deletion controllers/datadogagent/merger/rbac.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,20 @@ import (

corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/util/errors"
)

// RBACManager use to manage RBAC resources.
type RBACManager interface {
AddServiceAccount(namespace string, name string) error
AddServiceAccountByComponent(namespace, name, component string) error
AddPolicyRules(namespace string, roleName string, saName string, policies []rbacv1.PolicyRule) error
AddPolicyRulesByComponent(namespace string, roleName string, saName string, policies []rbacv1.PolicyRule, component string) error
AddClusterPolicyRules(namespace string, roleName string, saName string, policies []rbacv1.PolicyRule) error
AddClusterPolicyRulesByComponent(namespace string, roleName string, saName string, policies []rbacv1.PolicyRule, component string) error
DeleteServiceAccountByComponent(component, namespace string) error
DeleteRoleByComponent(component, namespace string) error
DeleteClusterRoleByComponent(component, namespace string) error
}

// NewRBACManager return new RBACManager instance
Expand All @@ -34,6 +41,10 @@ func NewRBACManager(store dependencies.StoreClient) RBACManager {
// rbacManagerImpl use to manage RBAC resources.
type rbacManagerImpl struct {
store dependencies.StoreClient

serviceAccountByComponent map[string][]string
roleByComponent map[string][]string
clusterRoleByComponent map[string][]string
}

// AddServiceAccount use to create a ServiceAccount
Expand All @@ -47,7 +58,30 @@ func (m *rbacManagerImpl) AddServiceAccount(namespace string, name string) error
return m.store.AddOrUpdate(kubernetes.ServiceAccountsKind, sa)
}

// AddPolicyRules use to add PolicyRules to a Role. It also create the RoleBinding.
// AddServiceAccountByComponent is used to create a ServiceAccount and associate it with a component
func (m *rbacManagerImpl) AddServiceAccountByComponent(namespace, name, component string) error {
m.serviceAccountByComponent[component] = append(m.serviceAccountByComponent[component], name)
return m.AddServiceAccount(namespace, name)
}

// DeleteServiceAccount use to remove a ServiceAccount from the store
func (m *rbacManagerImpl) DeleteServiceAccount(namespace string, name string) error {
found := m.store.Delete(kubernetes.ServiceAccountsKind, namespace, name)
if !found {
return fmt.Errorf("unable to delete ServiceAccount from the store because it was not found: %s/%s", namespace, name)
}
return nil
}

func (m *rbacManagerImpl) DeleteServiceAccountByComponent(component, namespace string) error {
errs := make([]error, 0, len(m.serviceAccountByComponent[component]))
for _, name := range m.serviceAccountByComponent[component] {
errs = append(errs, m.DeleteServiceAccount(namespace, name))
}
return errors.NewAggregate(errs)
}

// AddPolicyRules is used to add PolicyRules to a Role. It also creates the RoleBinding.
func (m *rbacManagerImpl) AddPolicyRules(namespace string, roleName string, saName string, policies []rbacv1.PolicyRule) error {
obj, _ := m.store.GetOrCreate(kubernetes.RolesKind, namespace, roleName)
role, ok := obj.(*rbacv1.Role)
Expand Down Expand Up @@ -93,6 +127,34 @@ func (m *rbacManagerImpl) AddPolicyRules(namespace string, roleName string, saNa
return nil
}

// AddPolicyRulesByComponent is used to add PolicyRules to a Role, create a RoleBinding, and associate them with a component
func (m *rbacManagerImpl) AddPolicyRulesByComponent(namespace string, roleName string, saName string, policies []rbacv1.PolicyRule, component string) error {
m.roleByComponent[component] = append(m.roleByComponent[component], roleName)
return m.AddPolicyRules(namespace, roleName, saName, policies)
}

// DeleteRole is used to delete a Role and RoleBinding.
func (m *rbacManagerImpl) DeleteRole(namespace string, roleName string) error {
found := m.store.Delete(kubernetes.RolesKind, namespace, roleName)
if !found {
return fmt.Errorf("unable to delete Role from the store because it was not found: %s/%s", namespace, roleName)
}

found = m.store.Delete(kubernetes.RoleBindingKind, namespace, roleName)
if !found {
return fmt.Errorf("unable to delete RoleBinding from the store because it was not found: %s/%s", namespace, roleName)
}
return nil
}

func (m *rbacManagerImpl) DeleteRoleByComponent(component, namespace string) error {
errs := make([]error, 0, len(m.roleByComponent[component]))
for _, name := range m.roleByComponent[component] {
errs = append(errs, m.DeleteRole(namespace, name))
}
return errors.NewAggregate(errs)
}

// AddClusterPolicyRules use to add PolicyRules to a ClusterRole. It also create the ClusterRoleBinding.
func (m *rbacManagerImpl) AddClusterPolicyRules(namespace string, roleName string, saName string, policies []rbacv1.PolicyRule) error {
obj, _ := m.store.GetOrCreate(kubernetes.ClusterRolesKind, "", roleName)
Expand Down Expand Up @@ -138,3 +200,32 @@ func (m *rbacManagerImpl) AddClusterPolicyRules(namespace string, roleName strin

return nil
}

// AddClusterPolicyRulesByComponent use to add PolicyRules to a ClusterRole. It also create the ClusterRoleBinding.
func (m *rbacManagerImpl) AddClusterPolicyRulesByComponent(namespace string, roleName string, saName string, policies []rbacv1.PolicyRule, component string) error {
m.clusterRoleByComponent[component] = append(m.clusterRoleByComponent[component], roleName)
return m.AddClusterPolicyRules(namespace, roleName, saName, policies)
}

// DeleteClusterRole is used to delete a ClusterRole and ClusterRoleBinding.
func (m *rbacManagerImpl) DeleteClusterRole(namespace string, roleName string) error {
found := m.store.Delete(kubernetes.ClusterRolesKind, namespace, roleName)
if !found {
return fmt.Errorf("unable to delete ClusterRole from the store because it was not found: %s/%s", namespace, roleName)
}

found = m.store.Delete(kubernetes.ClusterRoleBindingKind, namespace, roleName)
if !found {
return fmt.Errorf("unable to delete ClusterRoleBinding from the store because it was not found: %s/%s", namespace, roleName)
}

return nil
}

func (m *rbacManagerImpl) DeleteClusterRoleByComponent(component, namespace string) error {
errs := make([]error, 0, len(m.clusterRoleByComponent[component]))
for _, name := range m.clusterRoleByComponent[component] {
errs = append(errs, m.DeleteClusterRole(namespace, name))
}
return errors.NewAggregate(errs)
}
33 changes: 33 additions & 0 deletions controllers/datadogagent/override/dependencies.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016-present Datadog, Inc.

package override

import (
"k8s.io/apimachinery/pkg/util/errors"

"github.com/DataDog/datadog-operator/apis/datadoghq/v2alpha1"
apiutils "github.com/DataDog/datadog-operator/apis/utils"
"github.com/DataDog/datadog-operator/controllers/datadogagent/feature"
)

// Dependencies is used to override any resource/dependency settings with a v2alpha1.DatadogAgentComponentOverride.
func Dependencies(manager feature.ResourceManagers, overrides map[v2alpha1.ComponentName]*v2alpha1.DatadogAgentComponentOverride, namespace string) (errs []error) {
for component, override := range overrides {
errs = append(errs, overrideComponentDependencies(manager, override, component, namespace))
}
return errs
}

func overrideComponentDependencies(manager feature.ResourceManagers, override *v2alpha1.DatadogAgentComponentOverride, component v2alpha1.ComponentName, namespace string) error {
var errs []error
if !apiutils.BoolValue(override.CreateRbac) {
rbacManager := manager.RBACManager()
errs = append(errs, rbacManager.DeleteServiceAccountByComponent(string(component), namespace))
errs = append(errs, rbacManager.DeleteRoleByComponent(string(component), namespace))
errs = append(errs, rbacManager.DeleteClusterRoleByComponent(string(component), namespace))
}
return errors.NewAggregate(errs)
}

0 comments on commit 0c9f570

Please sign in to comment.