From 9b1e4abb084b6071b90b67a3f6a3ae227b5b3e08 Mon Sep 17 00:00:00 2001
From: petervo <petervo@redhat.com>
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