From 9b1e4abb084b6071b90b67a3f6a3ae227b5b3e08 Mon Sep 17 00:00:00 2001 From: petervo Date: Mon, 30 Oct 2017 15:14:07 -0700 Subject: [PATCH 4/6] kubernetes: Updates to work with 3.7 Cherry-pick two commits from master to work with OpenShift 3.7: - kubernetes: Use RBAC apiGroup when policybinds is not available (373e131e01e6) - kubernetes: Use forced to plain-text data in error messages (c45a335f59ad) Also cherry-pick "kubernetes: Fix kubeLoader $destroy callback" (c19604b9cb) as it's a nice and safe bug fix. --- pkg/kubernetes/scripts/kube-client-cockpit.js | 8 +- pkg/kubernetes/scripts/kube-client.js | 2 +- pkg/kubernetes/scripts/policy.js | 104 ++++++++++++++++++++------ pkg/kubernetes/scripts/projects.js | 35 +++------ pkg/kubernetes/scripts/registry.js | 4 +- 5 files changed, 102 insertions(+), 51 deletions(-) diff --git a/pkg/kubernetes/scripts/kube-client-cockpit.js b/pkg/kubernetes/scripts/kube-client-cockpit.js index 733326c..53ffd6f 100644 --- a/pkg/kubernetes/scripts/kube-client-cockpit.js +++ b/pkg/kubernetes/scripts/kube-client-cockpit.js @@ -38,7 +38,13 @@ try { obj = JSON.parse(response.data); } catch(e) { - return; + // Some kubernetes versions message up json reponses + if (response.data && response.headers && + response.headers["Content-Type"] === "text/plain") { + obj = { message: response.data }; + } else { + return; + } } if (obj && obj.message) diff --git a/pkg/kubernetes/scripts/kube-client.js b/pkg/kubernetes/scripts/kube-client.js index c2bae9b..39bb1cb 100644 --- a/pkg/kubernetes/scripts/kube-client.js +++ b/pkg/kubernetes/scripts/kube-client.js @@ -654,7 +654,7 @@ function connectUntil(ret, until) { if (until) { if (until.$on) { - until.$on("destroy", function() { + until.$on("$destroy", function() { ret.cancel(); }); } else { diff --git a/pkg/kubernetes/scripts/policy.js b/pkg/kubernetes/scripts/policy.js index 6ce3d23..074cdd1 100644 --- a/pkg/kubernetes/scripts/policy.js +++ b/pkg/kubernetes/scripts/policy.js @@ -32,13 +32,51 @@ '$rootScope', 'kubeLoader', 'kubeMethods', - function($q, $rootScope, loader, methods) { + 'kubeSelect', + 'KubeWatch', + 'KubeRequest', + 'KUBE_SCHEMA', + function($q, $rootScope, loader, methods, select, watch, KubeRequest, KUBE_SCHEMA) { + + var apiGroup; + var RBAC_GROUP = "rbac.authorization.k8s.io"; + var RBAC_API = "/apis/rbac.authorization.k8s.io/v1beta1"; + var POLICY_BINDING_API = KUBE_SCHEMA["RoleBinding"]["api"]; + var watchPromise; + + function setupRoleBinding(group) { + KUBE_SCHEMA["RoleBinding"]["api"] = group ? RBAC_API : POLICY_BINDING_API; + KUBE_SCHEMA["rolebindings"]["api"] = group ? RBAC_API : POLICY_BINDING_API; + apiGroup = group; + expireSAR(null); + expireWhoCan(null); + return group ? "rolebindings" : "policybindings"; + } + + function ensureWatchType() { + if (!watchPromise) { + watchPromise = new KubeRequest("GET", "/oapi/v1") + .then(function(response) { + var data = response.data || {}; + var i, l = data.resources || []; + for(i = 0; i < l.length; i++ ) { + if (l[i].kind == "PolicyBinding") + return setupRoleBinding(); + } + return setupRoleBinding(RBAC_GROUP); + }, function(err) { + console.warn("Error getting API", err); + return setupRoleBinding(); + }); + } + return watchPromise; + } /* * Data loading hacks: * - * We would like to watch rolebindings, but sadly that's not supported - * by origin. So we have to watch policybindings and then infer the + * We would like to watch rolebindings, but not all versions support + * that. So we have to watch policybindings and then infer the * rolebindings from there. * * In addition we would like to be able to load User and Group objects, @@ -47,6 +85,15 @@ */ loader.listen(function(present, removed) { var link, expire = { }; + + /* If reseting clear status */ + if (!present && !removed) { + expireSAR(null); + expireWhoCan(null); + watchPromise = null; + return; + } + for (link in removed) { if (removed[link].kind == "PolicyBinding") { update_rolebindings(removed[link].roleBindings, true); @@ -82,6 +129,10 @@ if (link in loader.objects) return; + /* Don't show system groups */ + if (subject.kind == "Group" && subject.name.indexOf("system:") === 0) + return; + /* An interim object, until perhaps the real thing can be loaded */ var interim = { kind: subject.kind, apiVersion: "v1", metadata: { name: subject.name } }; if (subject.namespace) @@ -236,7 +287,6 @@ var name = toName(role); var binding = { kind: "RoleBinding", - apiVersion: "v1", metadata: { name: name, namespace: namespace, @@ -246,7 +296,8 @@ groupNames: [], subjects: [], roleRef: { - name: role + name: role, + kind: "ClusterRole", } }; addToArray(roleArray(binding, "subjects"), subjects); @@ -255,6 +306,7 @@ } function removeFromRole(project, role, subject) { + subject.apiGroup = apiGroup; var namespace = toName(project); return modifyRole(namespace, role, function(data) { removeFromArray(roleArray(data, "subjects"), subject); @@ -268,26 +320,26 @@ }); } - function removeMemberFromPolicyBinding(policyBinding, project, subjectRoleBindings, subject) { + function removeMemberFromProject(project, subjectRoleBindings, subject) { var registryRoles = ["registry-admin", "registry-editor", "registry-viewer"]; var chain = $q.when(); - var defaultPolicybinding; var roleBindings = []; + var defaultPolicybinding = select().kind("PolicyBinding") + .namespace(project) + .name(":default").one(); + subject.apiGroup = apiGroup; - if(policyBinding && policyBinding.one()){ - defaultPolicybinding = policyBinding.one(); + if(defaultPolicybinding) roleBindings = defaultPolicybinding.roleBindings; - } - angular.forEach(subjectRoleBindings, function(o) { - angular.forEach(roleBindings, function(role) { - //Since we only added registry roles - //remove ONLY registry roles - if (( indexOf(registryRoles, role.name) !== -1) && role.name === o.metadata.name) { - chain = chain.then(function() { - return removeFromRole(project, role.name, subject); - }); - } - }); + + angular.forEach(subjectRoleBindings, function(role) { + //Since we only added registry roles + //remove ONLY registry roles + if (indexOf(registryRoles, role.roleRef.name) !== -1) { + chain = chain.then(function() { + return removeFromRole(project, role.roleRef.name, subject); + }); + } }); return chain; } @@ -334,14 +386,18 @@ return { watch: function watch(until) { - loader.watch("policybindings", until).then(function() { - expireWhoCan(null); + ensureWatchType().then(function (what) { + loader.watch(what, until) + .then(function() { + expireWhoCan(null); + }); }); }, whoCan: function whoCan(project, verb, resource) { return lookupWhoCan(toName(project), verb, resource); }, addToRole: function addToRole(project, role, subject) { + subject.apiGroup = apiGroup; var namespace = toName(project); return modifyRole(namespace, role, function(data) { addToArray(roleArray(data, "subjects"), subject); @@ -356,8 +412,8 @@ }); }, removeFromRole: removeFromRole, - removeMemberFromPolicyBinding: removeMemberFromPolicyBinding, - subjectAccessReview: subjectAccessReview, + removeMemberFromProject: removeMemberFromProject, + subjectAccessReview: subjectAccessReview }; } ]); diff --git a/pkg/kubernetes/scripts/projects.js b/pkg/kubernetes/scripts/projects.js index c616223..1067162 100644 --- a/pkg/kubernetes/scripts/projects.js +++ b/pkg/kubernetes/scripts/projects.js @@ -106,10 +106,11 @@ 'projectActions', 'ListingState', 'roleActions', - function($scope, $routeParams, $location, select, loader, projectData, projectAction, ListingState, roleAction) { + 'projectPolicy', + function($scope, $routeParams, $location, select, loader, projectData, projectAction, ListingState, roleAction, projectPolicy) { loader.watch("users", $scope); loader.watch("groups", $scope); - loader.watch("policybindings", $scope); + projectPolicy.watch($scope); var namespace = $routeParams["namespace"] || ""; $scope.projName = namespace; if (namespace) { @@ -149,9 +150,11 @@ 'projectActions', 'roleActions', 'ListingState', - function($scope, $routeParams, $location, select, loader, projectData, projectAction, roleActions, ListingState) { + 'projectPolicy', + function($scope, $routeParams, $location, select, loader, projectData, projectAction, roleActions, ListingState, projectPolicy) { loader.watch("users", $scope); loader.watch("groups", $scope); + projectPolicy.watch($scope); var user = $routeParams["user"] || ""; $scope.userName = user; if (user) { @@ -192,17 +195,14 @@ 'projectActions', 'roleActions', 'ListingState', - function($scope, $routeParams, $location, select, loader, projectData, projectAction, roleActions, ListingState) { + 'projectPolicy', + function($scope, $routeParams, $location, select, loader, projectData, projectAction, roleActions, ListingState, projectPolicy) { loader.watch("users", $scope); loader.watch("groups", $scope); + projectPolicy.watch($scope); var group = $routeParams["group"] || ""; $scope.groupName = group; if (group) { - var groupObj = select().kind("Group").name(group); - if (!groupObj || groupObj.length < 1) { - $scope.group = null; - return; - } $scope.group = function() { return select().kind("Group").name(group).one(); }; @@ -1154,9 +1154,6 @@ 'memberActions', "fields", function($q, $scope, kselect, loader, methods, projectData, projectPolicy, $location, memberActions, fields) { - function getPolicyBinding(namespace){ - return kselect().kind("PolicyBinding").namespace(namespace).name(":default"); - } function getMembers() { var members = []; var groups = getGroups(); @@ -1228,7 +1225,6 @@ }; function removeMemberFromParents(member) { var chain = $q.when(); - var policyBinding; var groups = projectData.getGroupsWithMember(getGroups(), member.metadata.name); angular.forEach(groups, function(g) { chain = chain.then(function() { @@ -1237,14 +1233,13 @@ }); var projects = projectData.getProjectsWithMember(getProjects(), member.metadata.name); angular.forEach(projects, function(project) { - policyBinding = getPolicyBinding(project.metadata.name); var subjectRoleBindings = projectData.subjectRoleBindings(member.metadata.name, project.metadata.name); var subject = { kind: member.kind, name: member.metadata.name, }; chain = chain.then(function() { - return projectPolicy.removeMemberFromPolicyBinding(policyBinding, + return projectPolicy.removeMemberFromProject( project.metadata.name, subjectRoleBindings, subject); }); }); @@ -1277,13 +1272,12 @@ //Project var member = $scope.fields.memberObj; var project = $scope.fields.parentObj.metadata.name; - var policyBinding = getPolicyBinding(project); var subjectRoleBindings = projectData.subjectRoleBindings(member.metadata.name, project); var subject = { kind: member.kind, name: member.metadata.name, }; - return projectPolicy.removeMemberFromPolicyBinding(policyBinding, project, subjectRoleBindings, subject); + return projectPolicy.removeMemberFromProject(project, subjectRoleBindings, subject); } }; @@ -1307,9 +1301,6 @@ function getProjects() { return kselect().kind("Project"); } - function getPolicyBinding(namespace){ - return kselect().kind("PolicyBinding").namespace(namespace).name(":default"); - } $scope.select = { member: 'Select Member', members: getUsers(), @@ -1319,17 +1310,15 @@ $scope.fields.grpProjects = projectData.getProjectsWithMember(getProjects(), fields.group.metadata.name); function removeMemberFromParents(member) { var chain = $q.when(); - var policyBinding; var projects = projectData.getProjectsWithMember(getProjects(), member.metadata.name); angular.forEach(projects, function(project) { - policyBinding = getPolicyBinding(project.metadata.name); var subjectRoleBindings = projectData.subjectRoleBindings(member.metadata.name, project.metadata.name); var subject = { kind: member.kind, name: member.metadata.name, }; chain = chain.then(function() { - return projectPolicy.removeMemberFromPolicyBinding(policyBinding, + return projectPolicy.removeMemberFromProject( project.metadata.name, subjectRoleBindings, subject); }); }); diff --git a/pkg/kubernetes/scripts/registry.js b/pkg/kubernetes/scripts/registry.js index c99cf16..780cc98 100644 --- a/pkg/kubernetes/scripts/registry.js +++ b/pkg/kubernetes/scripts/registry.js @@ -105,8 +105,8 @@ 'filterService', function($scope, loader, select, discoverSettings, imageData, imageActions, projectActions, projectData, projectPolicy, filter) { loader.load("projects"); - /* Watch the policybindings for project access changes */ - loader.watch("policybindings", $scope); + /* Watch the for project access changes */ + projectPolicy.watch($scope); /* * For now the dashboard has to watch all images in -- 2.14.3