Skip to content

Commit

Permalink
Collapse code between authorizationsync and migrate
Browse files Browse the repository at this point in the history
This change adds functions that handle all normalization, conversion and
comparison for the authorization objects.  These are now shared between
authorizationsync and `oadm migrate authorization` to prevent any logic
drift.

Signed-off-by: Monis Khan <[email protected]>
  • Loading branch information
enj committed Jun 8, 2017
1 parent b21d9bc commit 3e7035f
Show file tree
Hide file tree
Showing 6 changed files with 191 additions and 176 deletions.
137 changes: 133 additions & 4 deletions pkg/authorization/controller/authorizationsync/normalize.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,145 @@ package authorizationsync
import (
"strings"

authorizationapi "github.com/openshift/origin/pkg/authorization/api"

apiequality "k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/kubernetes/pkg/apis/rbac"

authorizationapi "github.com/openshift/origin/pkg/authorization/api"
)

// NormalizePolicyRules mutates the given rules and lowercases verbs, resources and API groups.
func NormalizeAndCompareClusterRole(originClusterRole *authorizationapi.ClusterRole, rbacClusterRole *rbac.ClusterRole) (*rbac.ClusterRole, bool, error) {
// convert the origin role to an rbac role and compare the results
convertedClusterRole := &rbac.ClusterRole{}
if err := authorizationapi.Convert_api_ClusterRole_To_rbac_ClusterRole(originClusterRole, convertedClusterRole, nil); err != nil {
return nil, false, err
}
// do a deep copy here since conversion does not guarantee a new object.
equivalentClusterRole := &rbac.ClusterRole{}
if err := rbac.DeepCopy_rbac_ClusterRole(convertedClusterRole, equivalentClusterRole, cloner); err != nil {
return nil, false, err
}

// normalize rules before persisting so RBAC's case sensitive authorizer will work
normalizePolicyRules(equivalentClusterRole.Rules)

// there's one wrinkle. If `openshift.io/reconcile-protect` is to true, then we must set rbac.authorization.kubernetes.io/autoupdate to false to
if equivalentClusterRole.Annotations["openshift.io/reconcile-protect"] == "true" {
equivalentClusterRole.Annotations["rbac.authorization.kubernetes.io/autoupdate"] = "false"
delete(equivalentClusterRole.Annotations, "openshift.io/reconcile-protect")
}

// prepare for create if needed
equivalentClusterRole.ResourceVersion = ""

// if the rbac role is nil, it is not equal
if rbacClusterRole == nil {
return equivalentClusterRole, false, nil
}

// if we might need to update, we need to stomp fields that are never going to match like uid and creation time
normalizeObjectMeta(&equivalentClusterRole.ObjectMeta, &rbacClusterRole.ObjectMeta)

// determine if they are equal
return equivalentClusterRole, apiequality.Semantic.DeepEqual(equivalentClusterRole, rbacClusterRole), nil
}

func NormalizeAndCompareClusterRoleBinding(originClusterRoleBinding *authorizationapi.ClusterRoleBinding, rbacClusterRoleBinding *rbac.ClusterRoleBinding) (*rbac.ClusterRoleBinding, bool, error) {
// convert the origin roleBinding to an rbac roleBinding and compare the results
convertedClusterRoleBinding := &rbac.ClusterRoleBinding{}
if err := authorizationapi.Convert_api_ClusterRoleBinding_To_rbac_ClusterRoleBinding(originClusterRoleBinding, convertedClusterRoleBinding, nil); err != nil {
return nil, false, err
}
// do a deep copy here since conversion does not guarantee a new object.
equivalentClusterRoleBinding := &rbac.ClusterRoleBinding{}
if err := rbac.DeepCopy_rbac_ClusterRoleBinding(convertedClusterRoleBinding, equivalentClusterRoleBinding, cloner); err != nil {
return nil, false, err
}

// prepare for create if needed
equivalentClusterRoleBinding.ResourceVersion = ""

// if the rbac roleBinding is nil, it is not equal
if rbacClusterRoleBinding == nil {
return equivalentClusterRoleBinding, false, nil
}

// if we might need to update, we need to stomp fields that are never going to match like uid and creation time
normalizeObjectMeta(&equivalentClusterRoleBinding.ObjectMeta, &rbacClusterRoleBinding.ObjectMeta)

// determine if they are equal
return equivalentClusterRoleBinding, apiequality.Semantic.DeepEqual(equivalentClusterRoleBinding, rbacClusterRoleBinding), nil
}

func NormalizeAndCompareRole(originRole *authorizationapi.Role, rbacRole *rbac.Role) (*rbac.Role, bool, error) {
// convert the origin role to an rbac role and compare the results
convertedRole := &rbac.Role{}
if err := authorizationapi.Convert_api_Role_To_rbac_Role(originRole, convertedRole, nil); err != nil {
return nil, false, err
}
// do a deep copy here since conversion does not guarantee a new object.
equivalentRole := &rbac.Role{}
if err := rbac.DeepCopy_rbac_Role(convertedRole, equivalentRole, cloner); err != nil {
return nil, false, err
}

// normalize rules before persisting so RBAC's case sensitive authorizer will work
normalizePolicyRules(equivalentRole.Rules)

// prepare for create if needed
equivalentRole.ResourceVersion = ""

// if the rbac role is nil, it is not equal
if rbacRole == nil {
return equivalentRole, false, nil
}

// if we might need to update, we need to stomp fields that are never going to match like uid and creation time
normalizeObjectMeta(&equivalentRole.ObjectMeta, &rbacRole.ObjectMeta)

// determine if they are equal
return equivalentRole, apiequality.Semantic.DeepEqual(equivalentRole, rbacRole), nil
}

func NormalizeAndCompareRoleBinding(originRoleBinding *authorizationapi.RoleBinding, rbacRoleBinding *rbac.RoleBinding) (*rbac.RoleBinding, bool, error) {
// convert the origin roleBinding to an rbac roleBinding and compare the results
convertedRoleBinding := &rbac.RoleBinding{}
if err := authorizationapi.Convert_api_RoleBinding_To_rbac_RoleBinding(originRoleBinding, convertedRoleBinding, nil); err != nil {
return nil, false, err
}
// do a deep copy here since conversion does not guarantee a new object.
equivalentRoleBinding := &rbac.RoleBinding{}
if err := rbac.DeepCopy_rbac_RoleBinding(convertedRoleBinding, equivalentRoleBinding, cloner); err != nil {
return nil, false, err
}

// prepare for create if needed
equivalentRoleBinding.ResourceVersion = ""

// if the rbac roleBinding is nil, it is not equal
if rbacRoleBinding == nil {
return equivalentRoleBinding, false, nil
}

// if we might need to update, we need to stomp fields that are never going to match like uid and creation time
normalizeObjectMeta(&equivalentRoleBinding.ObjectMeta, &rbacRoleBinding.ObjectMeta)

// determine if they are equal
return equivalentRoleBinding, apiequality.Semantic.DeepEqual(equivalentRoleBinding, rbacRoleBinding), nil
}

func normalizeObjectMeta(a, b *v1.ObjectMeta) {
a.SelfLink = b.SelfLink
a.UID = b.UID
a.ResourceVersion = b.ResourceVersion
a.CreationTimestamp = b.CreationTimestamp
}

// normalizePolicyRules mutates the given rules and lowercases verbs, resources and API groups.
// Origin's authorizer is case-insensitive to these fields but Kubernetes RBAC is not. Thus normalizing
// the Origin rules before persisting them as RBAC will allow authorization to continue to work.
func NormalizePolicyRules(rules []rbac.PolicyRule) {
func normalizePolicyRules(rules []rbac.PolicyRule) {
for i := range rules {
rule := &rules[i]
toLowerSlice(rule.Verbs)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import (
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/workqueue"
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/rbac"
rbacclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion"
rbacinformers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion/rbac/internalversion"
rbaclister "k8s.io/kubernetes/pkg/client/listers/rbac/internalversion"
Expand Down Expand Up @@ -75,50 +73,30 @@ func (c *OriginClusterRoleToRBACClusterRoleController) syncClusterRole(name stri
return c.rbacClient.ClusterRoles().Delete(name, nil)
}

// convert the origin role to an rbac role and compare the results
convertedClusterRole := &rbac.ClusterRole{}
if err := authorizationapi.Convert_api_ClusterRole_To_rbac_ClusterRole(originClusterRole, convertedClusterRole, nil); err != nil {
// determine if we need to create, update or do nothing
equivalentClusterRole, equal, err := NormalizeAndCompareClusterRole(originClusterRole, rbacClusterRole)
if err != nil {
return err
}
// do a deep copy here since conversion does not guarantee a new object.
equivalentClusterRole := &rbac.ClusterRole{}
if err := rbac.DeepCopy_rbac_ClusterRole(convertedClusterRole, equivalentClusterRole, cloner); err != nil {
return err
}

// normalize rules before persisting so RBAC's case sensitive authorizer will work
NormalizePolicyRules(equivalentClusterRole.Rules)

// there's one wrinkle. If `openshift.io/reconcile-protect` is to true, then we must set rbac.authorization.kubernetes.io/autoupdate to false to
if equivalentClusterRole.Annotations["openshift.io/reconcile-protect"] == "true" {
equivalentClusterRole.Annotations["rbac.authorization.kubernetes.io/autoupdate"] = "false"
delete(equivalentClusterRole.Annotations, "openshift.io/reconcile-protect")
}

// if we're missing the rbacClusterRole, create it
if apierrors.IsNotFound(rbacErr) {
equivalentClusterRole.ResourceVersion = ""
_, err := c.rbacClient.ClusterRoles().Create(equivalentClusterRole)
return err
}

// if we might need to update, we need to stomp fields that are never going to match like uid and creation time
equivalentClusterRole.SelfLink = rbacClusterRole.SelfLink
equivalentClusterRole.UID = rbacClusterRole.UID
equivalentClusterRole.ResourceVersion = rbacClusterRole.ResourceVersion
equivalentClusterRole.CreationTimestamp = rbacClusterRole.CreationTimestamp

// if they're equal, we have no work to do
if kapi.Semantic.DeepEqual(equivalentClusterRole, rbacClusterRole) {
if equal {
return nil
}

// they are not equal so we need to update
glog.V(1).Infof("writing RBAC clusterrole %v", name)
_, err := c.rbacClient.ClusterRoles().Update(equivalentClusterRole)
_, err = c.rbacClient.ClusterRoles().Update(equivalentClusterRole)
// if the update was invalid, we're probably changing an immutable field or something like that
// either way, the existing object is wrong. Delete it and try again.
if apierrors.IsInvalid(err) {
c.rbacClient.ClusterRoles().Delete(name, nil)
c.rbacClient.ClusterRoles().Delete(name, nil) // ignore delete error
}
return err
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import (
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/workqueue"
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/rbac"
rbacclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion"
rbacinformers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion/rbac/internalversion"
rbaclister "k8s.io/kubernetes/pkg/client/listers/rbac/internalversion"
Expand Down Expand Up @@ -75,41 +73,30 @@ func (c *OriginClusterRoleBindingToRBACClusterRoleBindingController) syncCluster
return c.rbacClient.ClusterRoleBindings().Delete(name, nil)
}

// convert the origin roleBinding to an rbac roleBinding and compare the results
convertedClusterRoleBinding := &rbac.ClusterRoleBinding{}
if err := authorizationapi.Convert_api_ClusterRoleBinding_To_rbac_ClusterRoleBinding(originClusterRoleBinding, convertedClusterRoleBinding, nil); err != nil {
return err
}
// do a deep copy here since conversion does not guarantee a new object.
equivalentClusterRoleBinding := &rbac.ClusterRoleBinding{}
if err := rbac.DeepCopy_rbac_ClusterRoleBinding(convertedClusterRoleBinding, equivalentClusterRoleBinding, cloner); err != nil {
// determine if we need to create, update or do nothing
equivalentClusterRoleBinding, equal, err := NormalizeAndCompareClusterRoleBinding(originClusterRoleBinding, rbacClusterRoleBinding)
if err != nil {
return err
}

// if we're missing the rbacClusterRoleBinding, create it
if apierrors.IsNotFound(rbacErr) {
equivalentClusterRoleBinding.ResourceVersion = ""
_, err := c.rbacClient.ClusterRoleBindings().Create(equivalentClusterRoleBinding)
return err
}

// if we might need to update, we need to stomp fields that are never going to match like uid and creation time
equivalentClusterRoleBinding.SelfLink = rbacClusterRoleBinding.SelfLink
equivalentClusterRoleBinding.UID = rbacClusterRoleBinding.UID
equivalentClusterRoleBinding.ResourceVersion = rbacClusterRoleBinding.ResourceVersion
equivalentClusterRoleBinding.CreationTimestamp = rbacClusterRoleBinding.CreationTimestamp

// if they're equal, we have no work to do
if kapi.Semantic.DeepEqual(equivalentClusterRoleBinding, rbacClusterRoleBinding) {
if equal {
return nil
}

// they are not equal so we need to update
glog.V(1).Infof("writing RBAC clusterrolebinding %v", name)
_, err := c.rbacClient.ClusterRoleBindings().Update(equivalentClusterRoleBinding)
_, err = c.rbacClient.ClusterRoleBindings().Update(equivalentClusterRoleBinding)
// if the update was invalid, we're probably changing an immutable field or something like that
// either way, the existing object is wrong. Delete it and try again.
if apierrors.IsInvalid(err) {
c.rbacClient.ClusterRoleBindings().Delete(name, nil)
c.rbacClient.ClusterRoleBindings().Delete(name, nil) // ignore delete error
}
return err
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import (
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/workqueue"
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/rbac"
rbacclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion"
rbacinformers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion/rbac/internalversion"
rbaclister "k8s.io/kubernetes/pkg/client/listers/rbac/internalversion"
Expand Down Expand Up @@ -80,44 +78,30 @@ func (c *OriginRoleToRBACRoleController) syncRole(key string) error {
return c.rbacClient.Roles(namespace).Delete(name, nil)
}

// convert the origin role to an rbac role and compare the results
convertedRole := &rbac.Role{}
if err := authorizationapi.Convert_api_Role_To_rbac_Role(originRole, convertedRole, nil); err != nil {
return err
}
// do a deep copy here since conversion does not guarantee a new object.
equivalentRole := &rbac.Role{}
if err := rbac.DeepCopy_rbac_Role(convertedRole, equivalentRole, cloner); err != nil {
// determine if we need to create, update or do nothing
equivalentRole, equal, err := NormalizeAndCompareRole(originRole, rbacRole)
if err != nil {
return err
}

// normalize rules before persisting so RBAC's case sensitive authorizer will work
NormalizePolicyRules(equivalentRole.Rules)

// if we're missing the rbacRole, create it
if apierrors.IsNotFound(rbacErr) {
equivalentRole.ResourceVersion = ""
_, err := c.rbacClient.Roles(namespace).Create(equivalentRole)
return err
}

// if we might need to update, we need to stomp fields that are never going to match like uid and creation time
equivalentRole.SelfLink = rbacRole.SelfLink
equivalentRole.UID = rbacRole.UID
equivalentRole.ResourceVersion = rbacRole.ResourceVersion
equivalentRole.CreationTimestamp = rbacRole.CreationTimestamp

// if they're equal, we have no work to do
if kapi.Semantic.DeepEqual(equivalentRole, rbacRole) {
if equal {
return nil
}

// they are not equal so we need to update
glog.V(1).Infof("writing RBAC role %v/%v", namespace, name)
_, err = c.rbacClient.Roles(namespace).Update(equivalentRole)
// if the update was invalid, we're probably changing an immutable field or something like that
// either way, the existing object is wrong. Delete it and try again.
if apierrors.IsInvalid(err) {
c.rbacClient.Roles(namespace).Delete(name, nil)
c.rbacClient.Roles(namespace).Delete(name, nil) // ignore delete error
}
return err
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import (
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/workqueue"
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/rbac"
rbacclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion"
rbacinformers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion/rbac/internalversion"
rbaclister "k8s.io/kubernetes/pkg/client/listers/rbac/internalversion"
Expand Down Expand Up @@ -80,41 +78,30 @@ func (c *OriginRoleBindingToRBACRoleBindingController) syncRoleBinding(key strin
return c.rbacClient.RoleBindings(namespace).Delete(name, nil)
}

// convert the origin roleBinding to an rbac roleBinding and compare the results
convertedRoleBinding := &rbac.RoleBinding{}
if err := authorizationapi.Convert_api_RoleBinding_To_rbac_RoleBinding(originRoleBinding, convertedRoleBinding, nil); err != nil {
return err
}
// do a deep copy here since conversion does not guarantee a new object.
equivalentRoleBinding := &rbac.RoleBinding{}
if err := rbac.DeepCopy_rbac_RoleBinding(convertedRoleBinding, equivalentRoleBinding, cloner); err != nil {
// determine if we need to create, update or do nothing
equivalentRoleBinding, equal, err := NormalizeAndCompareRoleBinding(originRoleBinding, rbacRoleBinding)
if err != nil {
return err
}

// if we're missing the rbacRoleBinding, create it
if apierrors.IsNotFound(rbacErr) {
equivalentRoleBinding.ResourceVersion = ""
_, err := c.rbacClient.RoleBindings(namespace).Create(equivalentRoleBinding)
return err
}

// if we might need to update, we need to stomp fields that are never going to match like uid and creation time
equivalentRoleBinding.SelfLink = rbacRoleBinding.SelfLink
equivalentRoleBinding.UID = rbacRoleBinding.UID
equivalentRoleBinding.ResourceVersion = rbacRoleBinding.ResourceVersion
equivalentRoleBinding.CreationTimestamp = rbacRoleBinding.CreationTimestamp

// if they're equal, we have no work to do
if kapi.Semantic.DeepEqual(equivalentRoleBinding, rbacRoleBinding) {
if equal {
return nil
}

// they are not equal so we need to update
glog.V(1).Infof("writing RBAC rolebinding %v/%v", namespace, name)
_, err = c.rbacClient.RoleBindings(namespace).Update(equivalentRoleBinding)
// if the update was invalid, we're probably changing an immutable field or something like that
// either way, the existing object is wrong. Delete it and try again.
if apierrors.IsInvalid(err) {
c.rbacClient.RoleBindings(namespace).Delete(name, nil)
c.rbacClient.RoleBindings(namespace).Delete(name, nil) // ignore delete error
}
return err
}
Expand Down
Loading

0 comments on commit 3e7035f

Please sign in to comment.