Skip to content

Commit

Permalink
feat: Ensure CRD are up to date and create cluster roles and cluster …
Browse files Browse the repository at this point in the history
…role binding using namespace unique name (#815)

* Check if CRDs are up to date in cluster and create/update/delete cluster roles and cluster rolebindings

Signed-off-by: flacatus <flacatus@redhat.com>

* Fixes

Signed-off-by: flacatus <flacatus@redhat.com>

* Add fixes to update/delete clusterroles and clusterrolebindings. Also add improves to CRD checks

Signed-off-by: flacatus <flacatus@redhat.com>

* Fix CRD code gaps

Signed-off-by: flacatus <flacatus@redhat.com>

* Update createClusterRoleFromFile method

Signed-off-by: flacatus <flacatus@redhat.com>

* Fixes

Signed-off-by: flacatus <flacatus@redhat.com>

* Fix gaps

Signed-off-by: flacatus <flacatus@redhat.com>

* Add method names fixes

Signed-off-by: flacatus <flacatus@redhat.com>

* Revert update/delete title and add titles for legacy cluster roles

Signed-off-by: flacatus <flacatus@redhat.com>
  • Loading branch information
flacatus committed Aug 25, 2020
1 parent de0599c commit 2fca137
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 28 deletions.
21 changes: 20 additions & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,25 @@
"<node_internals>/**"
]
},
{
"type": "node",
"request": "launch",
"name": "Debug Openshift Install",
"program": "${workspaceFolder}/bin/run",
"args": [
"server:start",
"--platform",
"openshift",
"--installer",
"operator"
],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"disableOptimisticBPs": true,
"windows": {
"program": "${workspaceFolder}/node_modules/jest/bin/jest",
}
},
{
"type": "node",
"request": "launch",
Expand Down Expand Up @@ -40,6 +59,6 @@
"program": "${workspaceFolder}/node_modules/jest/bin/jest",
}
},

]
}
42 changes: 38 additions & 4 deletions src/api/kube.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,9 +269,18 @@ export class KubeHelper {
}
}

async createClusterRoleFromFile(filePath: string) {
async createClusterRoleFromFile(filePath: string, roleName?: string) {
const yamlRole = this.safeLoadFromYamlFile(filePath) as V1ClusterRole
const k8sRbacAuthApi = KubeHelper.KUBE_CONFIG.makeApiClient(RbacAuthorizationV1Api)
if (!yamlRole.metadata) {
yamlRole.metadata = {}
}

if (roleName) {
yamlRole.metadata.name = roleName
} else if (!yamlRole.metadata.name) {
throw new Error(`Role name is not specified in ${filePath}`)
}
try {
const res = await k8sRbacAuthApi.createClusterRole(yamlRole)
return res.response.statusCode
Expand All @@ -284,11 +293,17 @@ export class KubeHelper {
}
}

async replaceClusterRoleFromFile(filePath: string) {
async replaceClusterRoleFromFile(filePath: string, roleName?: string) {
const yamlRole = this.safeLoadFromYamlFile(filePath) as V1ClusterRole
const k8sRbacAuthApi = KubeHelper.KUBE_CONFIG.makeApiClient(RbacAuthorizationV1Api)
if (!yamlRole.metadata || !yamlRole.metadata.name) {
throw new Error(`Cluster Role read from ${filePath} must have name specified`)
if (!yamlRole.metadata) {
yamlRole.metadata = {}
}

if (roleName) {
yamlRole.metadata.name = roleName
} else if (!yamlRole.metadata.name) {
throw new Error(`Role name is not specified in ${filePath}`)
}
try {
const res = await k8sRbacAuthApi.replaceClusterRole(yamlRole.metadata.name, yamlRole)
Expand Down Expand Up @@ -1096,6 +1111,25 @@ export class KubeHelper {
}
}

async isCRDCompatible(crdClusterName: string, crdFilePath: string): Promise<boolean> {
const { spec: crdFileSpec } = this.safeLoadFromYamlFile(crdFilePath) as V1beta1CustomResourceDefinition
const { spec: crdClusterSpec } = await this.getCrd(crdClusterName)
const { validation: { openAPIV3Schema : { properties: crdFileProps = '' } = {} } = {} } = crdFileSpec
const { validation: { openAPIV3Schema : { properties: crdClusterProps = '' } = {} } = {} } = crdClusterSpec

if (!crdFileSpec.versions || !crdClusterSpec.versions) {
return false
}

const crdFileVersions = crdFileSpec.versions.find(e => e.storage === true)
const crdClusterVersions = crdClusterSpec.versions.find(e => e.storage === true)
if (!crdFileVersions || !crdClusterVersions || crdFileVersions.name !== crdClusterVersions.name) {
return false
}

return Object.keys(crdFileProps).length === Object.keys(crdClusterProps).length
}

async ingressExist(name = '', namespace = ''): Promise<boolean> {
const k8sExtensionsApi = KubeHelper.KUBE_CONFIG.makeApiClient(ExtensionsV1beta1Api)
try {
Expand Down
82 changes: 59 additions & 23 deletions src/tasks/installers/operator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ export class OperatorTasks {
* Returns tasks list which perform preflight platform checks.
*/
startTasks(flags: any, command: Command): Listr {
const clusterRoleName = `${flags.chenamespace}-${this.operatorClusterRole}`
const clusterRoleBindingName = `${flags.chenamespace}-${this.operatorClusterRoleBinding}`
const kube = new KubeHelper(flags)
if (isStableVersion(flags)) {
command.warn('Consider using the more reliable \'OLM\' installer when deploying a stable release of Eclipse Che (--installer=olm).')
Expand Down Expand Up @@ -70,14 +72,14 @@ export class OperatorTasks {
}
},
{
title: `Create ClusterRole ${this.operatorClusterRole}`,
title: `Create ClusterRole ${clusterRoleName}`,
task: async (ctx: any, task: any) => {
const exist = await kube.clusterRoleExist(this.operatorClusterRole)
const exist = await kube.clusterRoleExist(clusterRoleName)
if (exist) {
task.title = `${task.title}...It already exists.`
} else {
const yamlFilePath = ctx.resourcesPath + 'cluster_role.yaml'
const statusCode = await kube.createClusterRoleFromFile(yamlFilePath)
const statusCode = await kube.createClusterRoleFromFile(yamlFilePath, clusterRoleName)
if (statusCode === 403) {
command.error('ERROR: It looks like you don\'t have enough privileges. You need to grant more privileges to current user or use a different user. If you are using minishift you can "oc login -u system:admin"')
}
Expand All @@ -99,13 +101,13 @@ export class OperatorTasks {
}
},
{
title: `Create ClusterRoleBinding ${this.operatorClusterRoleBinding}`,
title: `Create ClusterRoleBinding ${clusterRoleBindingName}`,
task: async (_ctx: any, task: any) => {
const exist = await kube.clusterRoleBindingExist(this.operatorRoleBinding)
const exist = await kube.clusterRoleBindingExist(clusterRoleBindingName)
if (exist) {
task.title = `${task.title}...It already exists.`
} else {
await kube.createClusterRoleBinding(this.operatorClusterRoleBinding, this.operatorServiceAccount, flags.chenamespace, this.operatorClusterRole)
await kube.createClusterRoleBinding(clusterRoleBindingName, this.operatorServiceAccount, flags.chenamespace, clusterRoleName)
task.title = `${task.title}...done.`
}
}
Expand All @@ -114,10 +116,15 @@ export class OperatorTasks {
title: `Create CRD ${this.cheClusterCrd}`,
task: async (ctx: any, task: any) => {
const exist = await kube.crdExist(this.cheClusterCrd)
const yamlFilePath = ctx.resourcesPath + 'crds/org_v1_che_crd.yaml'

if (exist) {
const checkCRD = await kube.isCRDCompatible(this.cheClusterCrd, yamlFilePath)
if (!checkCRD) {
cli.error(`It is not possible to proceed the installation of Eclipse Che. The existed ${this.cheClusterCrd} is different from a new one. Please update it to continue the installation.`)
}
task.title = `${task.title}...It already exists.`
} else {
const yamlFilePath = ctx.resourcesPath + 'crds/org_v1_che_crd.yaml'
await kube.createCrdFromFile(yamlFilePath)
task.title = `${task.title}...done.`
}
Expand Down Expand Up @@ -171,6 +178,8 @@ export class OperatorTasks {

updateTasks(flags: any, command: Command): Listr {
const kube = new KubeHelper(flags)
const clusterRoleName = `${flags.chenamespace}-${this.operatorClusterRole}`
const clusterRoleBindingName = `${flags.chenamespace}-${this.operatorClusterRoleBinding}`
return new Listr([
copyOperatorResources(flags, command.config.cacheDir),
{
Expand Down Expand Up @@ -208,18 +217,26 @@ export class OperatorTasks {
}
},
{
title: `Updating ClusterRole ${this.operatorClusterRole}`,
title: `Updating ClusterRole ${clusterRoleName}`,
task: async (ctx: any, task: any) => {
const exist = await kube.clusterRoleExist(this.operatorClusterRole)
const clusterRoleExists = await kube.clusterRoleExist(clusterRoleName)
const legacyClusterRoleExists = await kube.clusterRoleExist(this.operatorClusterRole)
const yamlFilePath = ctx.resourcesPath + 'cluster_role.yaml'
if (exist) {
const statusCode = await kube.replaceClusterRoleFromFile(yamlFilePath)
if (clusterRoleExists) {
const statusCode = await kube.replaceClusterRoleFromFile(yamlFilePath, clusterRoleName)
if (statusCode === 403) {
command.error('ERROR: It looks like you don\'t have enough privileges. You need to grant more privileges to current user or use a different user. If you are using minishift you can "oc login -u system:admin"')
}
task.title = `${task.title}...updated.`
// it is needed to check the legacy cluster object name to be compatible with previous installations
} else if (legacyClusterRoleExists) {
const statusCode = await kube.replaceClusterRoleFromFile(yamlFilePath, this.operatorClusterRole)
if (statusCode === 403) {
command.error('ERROR: It looks like you don\'t have enough privileges. You need to grant more privileges to current user or use a different user. If you are using minishift you can "oc login -u system:admin"')
}
task.title = `Updating ClusterRole ${this.operatorClusterRole}...updated.`
} else {
const statusCode = await kube.createClusterRoleFromFile(yamlFilePath)
const statusCode = await kube.createClusterRoleFromFile(yamlFilePath, clusterRoleName)
if (statusCode === 403) {
command.error('ERROR: It looks like you don\'t have enough privileges. You need to grant more privileges to current user or use a different user. If you are using minishift you can "oc login -u system:admin"')
}
Expand All @@ -242,14 +259,19 @@ export class OperatorTasks {
}
},
{
title: `Updating ClusterRoleBinding ${this.operatorClusterRoleBinding}`,
title: `Updating ClusterRoleBinding ${clusterRoleBindingName}`,
task: async (_ctx: any, task: any) => {
const exist = await kube.clusterRoleBindingExist(this.operatorRoleBinding)
if (exist) {
await kube.replaceClusterRoleBinding(this.operatorClusterRoleBinding, this.operatorServiceAccount, flags.chenamespace, this.operatorClusterRole)
const clusterRoleBindExists = await kube.clusterRoleBindingExist(clusterRoleBindingName)
const legacyClusterRoleBindExists = await kube.clusterRoleBindingExist(this.operatorClusterRoleBinding)
if (clusterRoleBindExists) {
await kube.replaceClusterRoleBinding(clusterRoleBindingName, this.operatorServiceAccount, flags.chenamespace, clusterRoleName)
task.title = `${task.title}...updated.`
// it is needed to check the legacy cluster object name to be compatible with previous installations
} else if (legacyClusterRoleBindExists) {
await kube.replaceClusterRoleBinding(this.operatorClusterRoleBinding, this.operatorServiceAccount, flags.chenamespace, this.operatorClusterRole)
task.title = `Updating ClusterRoleBinding ${this.operatorClusterRoleBinding}...updated.`
} else {
await kube.createClusterRoleBinding(this.operatorClusterRoleBinding, this.operatorServiceAccount, flags.chenamespace, this.operatorClusterRole)
await kube.createClusterRoleBinding(clusterRoleBindingName, this.operatorServiceAccount, flags.chenamespace, clusterRoleName)
task.title = `${task.title}...created new one.`
}
}
Expand Down Expand Up @@ -307,6 +329,8 @@ export class OperatorTasks {
*/
deleteTasks(flags: any): ReadonlyArray<Listr.ListrTask> {
let kh = new KubeHelper(flags)
const clusterRoleName = `${flags.chenamespace}-${this.operatorClusterRole}`
const clusterRoleBindingName = `${flags.chenamespace}-${this.operatorClusterRoleBinding}`
return [{
title: `Delete the Custom Resource of type ${CHE_CLUSTER_CRD}`,
task: async (_ctx: any, task: any) => {
Expand Down Expand Up @@ -334,21 +358,33 @@ export class OperatorTasks {
}
},
{
title: `Delete cluster role binding ${this.operatorClusterRoleBinding}`,
title: `Delete cluster role binding ${clusterRoleBindingName}`,
task: async (_ctx: any, task: any) => {
if (await kh.clusterRoleBindingExist(this.operatorClusterRoleBinding)) {
const clusterRoleBindExists = await kh.clusterRoleBindingExist(clusterRoleBindingName)
const legacyClusterRoleBindExists = await kh.clusterRoleBindingExist(this.operatorClusterRoleBinding)
if (clusterRoleBindExists) {
await kh.deleteClusterRoleBinding(clusterRoleBindingName)
task.title = await `${task.title}...OK`
// it is needed to check the legacy cluster object name to be compatible with previous installations
} else if (legacyClusterRoleBindExists) {
await kh.deleteClusterRoleBinding(this.operatorClusterRoleBinding)
task.title = await `Delete cluster role binding ${this.operatorClusterRoleBinding}...OK`
}
task.title = await `${task.title}...OK`
}
},
{
title: `Delete cluster role ${this.operatorClusterRole}`,
title: `Delete cluster role ${clusterRoleName}`,
task: async (_ctx: any, task: any) => {
if (await kh.clusterRoleExist(this.operatorClusterRole)) {
const clusterRoleExists = await kh.clusterRoleExist(clusterRoleName)
const legacyClusterRoleExists = await kh.clusterRoleExist(this.operatorClusterRole)
if (clusterRoleExists) {
await kh.deleteClusterRole(clusterRoleName)
task.title = await `${task.title}...OK`
// it is needed to check the legacy cluster object name to be compatible with previous installations
} else if (legacyClusterRoleExists) {
await kh.deleteClusterRole(this.operatorClusterRole)
task.title = await `Delete cluster role ${this.operatorClusterRole}...OK`
}
task.title = await `${task.title}...OK`
}
},
{
Expand Down

0 comments on commit 2fca137

Please sign in to comment.