From a122457bfcca1f0ec782d79f38888b87a22d9ec4 Mon Sep 17 00:00:00 2001 From: Humair Khan Date: Thu, 2 Dec 2021 16:24:24 -0500 Subject: [PATCH] Correct inconsistencies in notebook for proj onboarding. --- pages/onboarding_project.ipynb | 508 +++++++++++++++++++++++++++------ 1 file changed, 413 insertions(+), 95 deletions(-) diff --git a/pages/onboarding_project.ipynb b/pages/onboarding_project.ipynb index 6f2881d..c887688 100644 --- a/pages/onboarding_project.ipynb +++ b/pages/onboarding_project.ipynb @@ -6,36 +6,76 @@ "tags": [] }, "source": [ - "# How to onboard a new project to a cluster on Operate First\n", - "This guide explains how you can onboard a new project to one of the Operate First clusters. \n", - "\n", - ":::{seealso}\n", - "\n", - "For more information about the Operate First see [Getting started with Operate First](references:operate-first-start)\n", - ":::\n", - "\n", + "# How to onboard a new project to a cluster on Operate First Cloud\n", + "This guide explains how you can onboard a new project to one of the Operate First clusters. " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ "## Prerequisites\n", - "- There are no prerequisites.\n", - "\n", + "- Have a team name ready\n", + "- Have an OpenShift namespace name ready \n", + "- Have a list of GitHub user accounts that require access\n", + "- If running this notebook locally, you need to have the following tools installed: \n", "\n", + " - [opfcli](https://github.com/operate-first/opfcli/releases)\n", + " - [kustomize](https://kubectl.docs.kubernetes.io/installation/kustomize/binaries/)\n", + " - [yq](https://github.com/mikefarah/yq#install)\n", + " - [python](https://www.python.org/)\n", + " - [python json](https://docs.python.org/3/library/json.html)\n", + "- Know which cluster you are looking to provision an OpenShift namespace to\n", + " \n", + " Run the following command to see the list of available Clusters:" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "balrog\n", + "curator\n", + "demo\n", + "ocp-prod\n", + "ocp-staging\n", + "osc-cl1\n", + "rick\n", + "smaug\n" + ] + } + ], + "source": [ + "!cd /tmp && kustomize build https://github.com/operate-first/apps/acm/overlays/moc/infra/managedclusters?ref=master | yq e -N '.metadata.name' -" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "## Outcomes\n", "\n", "- [ ] A pull request against the `operate-first/apps` repository.\n", - "\n", - "The PR enables Operate First to:\n", - "\n", "- [ ] Create a namespace on the desired Operate First cluster.\n", - "- [ ] Add desired users as namespace admins to the specified namespace.\n", - "\n", - "## Introduction\n", - "\n", - "All manifests for all the workloads owned by Operate First Ops team are maintained in the `operate-first/apps` repository following the [Kustomize best practices](https://kubernetes.io/docs/tasks/manage-kubernetes-objects/kustomization/).\n", - "\n", - "The `cluster-scope` folder in this repo stores all privileged resources that are usually not allowed to be deployed by regular project admin and requires elevated access like cluster-admin role.\n", - "\n", - "If you want to know more about the overall design please consult Operate First's [Architectural Decision Records (ADR) archive](https://www.operate-first.cloud/blueprints/blueprint/#architectural-decisions).\n", - "\n", - "\n", + "- [ ] Create an OCP group with your team's GH users as members. \n", + "- [ ] Provide project admin level access to the newly created namespace for this group." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ "## Steps" ] }, @@ -45,31 +85,41 @@ "tags": [] }, "source": [ - "### 1. Define important variables\n", + "### 1. Enter your info\n", "\n", - "In this guide we will use a couple of facts about the project. To make it easier to follow this guide, let's define these values beforehand." + "In this guide we will use a couple of facts about your team and project. To make it easier to follow this guide, let's define these values beforehand." ] }, { "cell_type": "code", - "execution_count": 2, - "metadata": {}, + "execution_count": 54, + "metadata": { + "tags": [] + }, "outputs": [], "source": [ "import os\n", "import json\n", + "import tempfile\n", + "import yaml\n", "\n", "# User variables\n", - "GITHUB_USERNAME = os.getenv(\"JUPYTERHUB_USER\") # If this notebook is executed within Jupyter Hub on Operate First, you can use the `JUPYTERHUB_USER` variables instead\n", + "GITHUB_USERNAME = \"HumairAK\" # If this notebook is executed within Jupyter Hub on Operate First, you can use the `JUPYTERHUB_USER` variables instead\n", "\n", "# Namespace specific variables\n", - "NAMESPACE_NAME=\"demo-project\"\n", + "NAMESPACE_NAME=\"demo-projecx\"\n", "NAMESPACE_DISPLAY_NAME=\"Demo Project Namespace\"\n", - "TEAM_NAME=\"demo-team\"\n", + "TEAM_NAME=\"demo-teamx\"\n", + "\n", + "# Pick a Quota from: x-small, small, medium, large. \n", + "# See details here: https://www.operate-first.cloud/apps/content/cluster-scope/quotas.html\n", + "QUOTA=\"small\"\n", + "# If instead you want to use a custom quota, ignore QUOTA and set the following to True\n", + "CUSTOM_QUOTA=True\n", "\n", "# Target cluster variables\n", - "TARGET_CLUSTER_NAME = \"demo\"\n", - "TARGET_CLUSTER_REGION = \"emea\"\n", + "TARGET_CLUSTER_NAME = \"smaug\"\n", + "TARGET_CLUSTER_REGION = \"moc\"\n", "\n", "NAMESPACE_ADMINS_LST = [GITHUB_USERNAME,] # list of LOWERCASE github usernames of the namespace admins\n", "\n", @@ -80,119 +130,351 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "tags": [] + }, "source": [ - "\n", "### 2. Fork and clone the apps repository\n", "\n", "Please fork/clone the [operate-first/apps](https://github.com/operate-first/apps) repository. We’ll be working within this repository only.\n", "\n", + "> Note: If you already have a forked & clone repository, please ensure your master branch is up to date with upstream master.\n", + "\n", "1. Go to [operate-first/apps](https://github.com/operate-first/apps).\n", - "2. Click on a fork button.\n", - "3. When a fork is created click on the code button and copy an address of your forked repository.\n", - "4. Run following command using copied address:" + "2. Click on the fork button, this will fork the repo to your GitHub account.\n", + "3. Run the commands below to clone the forked repo." ] }, { "cell_type": "code", - "execution_count": 3, - "metadata": {}, + "execution_count": 55, + "metadata": { + "tags": [] + }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Cloning into 'apps'...\n", - "remote: Enumerating objects: 12377, done.\u001b[K\n", - "remote: Counting objects: 100% (1658/1658), done.\u001b[K\n", - "remote: Compressing objects: 100% (877/877), done.\u001b[K\n", - "remote: Total 12377 (delta 743), reused 1561 (delta 686), pack-reused 10719\u001b[K\n", - "Receiving objects: 100% (12377/12377), 2.63 MiB | 4.05 MiB/s, done.\n", - "Resolving deltas: 100% (6003/6003), done.\n", - "/home/anand/Documents/4n4nd/hitchhikers-guide/pages/apps\n" + "Working in directory $/tmp/tmpjmgbmtb6\n", + "Cloning into '/tmp/tmpjmgbmtb6'...\n", + "remote: Enumerating objects: 16488, done.\u001b[K\n", + "remote: Counting objects: 100% (3586/3586), done.\u001b[K\n", + "remote: Compressing objects: 100% (1533/1533), done.\u001b[K\n", + "remote: Total 16488 (delta 2202), reused 3046 (delta 1937), pack-reused 12902\u001b[K\n", + "Receiving objects: 100% (16488/16488), 7.24 MiB | 18.29 MiB/s, done.\n", + "Resolving deltas: 100% (8219/8219), done.\n" ] } ], "source": [ - "!git clone https://github.com/{GITHUB_USERNAME}/apps.git\n", - "%cd apps" + "WORKDIR=tempfile.mkdtemp()\n", + "!echo Working in directory ${WORKDIR}\n", + "!git clone https://github.com/{GITHUB_USERNAME}/apps.git {WORKDIR}" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "tags": [] + }, "source": [ - "### 3. Adding namespaces\n", + "### 3. Create Base resources\n", + "\n", + "We store all our generic configurations in a `base` location, from where we selectively choose and deploy assets to target clusters. In this case, we need to create an [namespace][ns], [group][grp], and a project [rolebinding][rb] for your team. To do this we'll use the `opfcli` to help us out.\n", "\n", - "For easier onboarding to ArgoCD later on, we prefer to follow a name pattern for all our namespaces. Please use your team name as a prefix to the namespace name like so: `$TEAM_NAME-example-project`.\n", - "#### Base resources\n" + "[ns]: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/\n", + "[grp]: https://kubernetes.io/docs/reference/access-authn-authz/rbac/\n", + "[rb]: https://kubernetes.io/docs/reference/access-authn-authz/rbac/" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/tmp/tmpjmgbmtb6\n", + "\u001b[36mINFO\u001b[0m[0000] writing group definition to /tmp/tmpjmgbmtb6/cluster-scope/base/user.openshift.io/groups/demo-teamx \n", + "\u001b[36mINFO\u001b[0m[0000] writing rbac definition to /tmp/tmpjmgbmtb6/cluster-scope/components/project-admin-rolebindings/demo-teamx \n", + "\u001b[36mINFO\u001b[0m[0000] writing namespace definition to /tmp/tmpjmgbmtb6/cluster-scope/base/core/namespaces/demo-projecx \n" + ] + } + ], + "source": [ + "%cd {WORKDIR}\n", + "!opfcli create-project {NAMESPACE_NAME} {TEAM_NAME} -d \"{NAMESPACE_DISPLAY_NAME}\"" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "tags": [] + }, "source": [ - "To create necessary resources use opfcli which creates the following folders with files:\n", + "### 4. Adding namespace resources to the target cluster\n", "\n", - "- A namespace in `cluster-scope/base/core/namespaces/$NAMESPACE_NAME`.\n", - "- A blank user group for the `$TEAM_NAME` if it does not exist yet in the `cluster-scope/base/user.openshift.io/groups/$TEAM_NAME`.\n", - "- An RBAC component for the project admin role `RoleBinding` in `cluster-scope/components/namespace-admin-rolebinding/$TEAM_NAME` and maps it to the newly added namespace." + "Run the following code to ensure your namespace is created in the target cluster." ] }, { "cell_type": "code", - "execution_count": 4, - "metadata": {}, + "execution_count": 57, + "metadata": { + "tags": [] + }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "\u001b[36mINFO\u001b[0m[0000] writing group definition to /home/anand/Documents/4n4nd/hitchhikers-guide/pages/apps/cluster-scope/base/user.openshift.io/groups/demo-team \n", - "\u001b[36mINFO\u001b[0m[0000] writing rbac definition to /home/anand/Documents/4n4nd/hitchhikers-guide/pages/apps/cluster-scope/components/project-admin-rolebindings/demo-team \n", - "\u001b[36mINFO\u001b[0m[0000] writing namespace definition to /home/anand/Documents/4n4nd/hitchhikers-guide/pages/apps/cluster-scope/base/core/namespaces/demo-project \n" + "/tmp/tmpjmgbmtb6/cluster-scope/overlays/prod/moc/smaug\n" ] } ], "source": [ - "!opfcli create-project {NAMESPACE_NAME} {TEAM_NAME} -d \"{NAMESPACE_DISPLAY_NAME}\"" + "%cd {WORKDIR}/cluster-scope/overlays/prod/{TARGET_CLUSTER}/\n", + "!kustomize edit add resource ../../../../base/core/namespaces/{NAMESPACE_NAME}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### 4. Adding namespace resources to the target cluster\n", - "\n", - "The following cell will add `../../../../base/core/namespaces/$NAMESPACE_NAME` as a resource to the kusomization for your target cluster (`cluster-scope/overlays/prod/$TARGET_CLUSTER/kustomization.yaml`).\n", + "The line added above by `kustomize edit` does not add the entry alphabetically, so we are going to sort it here ourselves (for human readability), you can also do it manually." + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "kustomization_path = WORKDIR + \"/cluster-scope/overlays/prod/\" + TARGET_CLUSTER + \"/kustomization.yaml\"\n", + "with open(kustomization_path, \"r\") as f:\n", + " kustomization = yaml.safe_load(f)\n", + " kustomization['resources'].sort()\n", + "with open(kustomization_path, 'w') as f:\n", + " yaml.dump(kustomization, f)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "### 5. Add your quota to the namespace\n", "\n", - "\n" + "With the namespace manifest created, we now want to ensure the appropriate quota is added. If you picked the custom quota option you can skip to the next code block." ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 59, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/tmp/tmpjmgbmtb6/cluster-scope/base/core/namespaces/demo-projecx\n" + ] + } + ], + "source": [ + "%cd {WORKDIR}/cluster-scope/base/core/namespaces/{NAMESPACE_NAME}\n", + "if(not CUSTOM_QUOTA): \n", + " !kustomize edit add component ../../../../components/resourcequotas/{QUOTA}" + ] + }, + { + "cell_type": "markdown", "metadata": {}, + "source": [ + "If you require a custom quota, please enter the values for the resources you require:" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": { + "tags": [] + }, "outputs": [], "source": [ - "!cd cluster-scope/overlays/prod/{TARGET_CLUSTER}/ && kustomize edit add resource ../../../../base/core/namespaces/{NAMESPACE_NAME}" + "CPU_LIMIT = \"1\"\n", + "CPU_REQUESTS = \"1\"\n", + "MEMORY_LIMIT = \"4Gi\"\n", + "MEMORY_REQUEST = \"4Gi\"\n", + "STORAGE = \"10Gi\"\n", + "NUMBER_OF_BUCKETS = 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### 5. Populate your team\n", - "By executing the next cell, you will add the users listed in `$NAMESPACE_ADMINS_LST` to the OpenShift group `$TEAM_NAME` making them namespace admins for the above added namespace." + "Now run the following code to create the custom quota:" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 68, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/tmp/tmpjmgbmtb6/cluster-scope\n" + ] + } + ], + "source": [ + "%cd {WORKDIR}/cluster-scope\n", + "if CUSTOM_QUOTA:\n", + " custom_quota_path = \"base/core/namespaces/{0}/resourcequota.yaml\".format(NAMESPACE_NAME)\n", + " custom_quota = yaml.safe_load(\n", + " \"\"\"\n", + " apiVersion: v1\n", + " kind: ResourceQuota\n", + " metadata:\n", + " name: {0}-custom\n", + " spec:\n", + " hard:\n", + " limits.cpu: {1}\n", + " limits.memory: {2}\n", + " requests.cpu: {3}\n", + " requests.memory: {4}\n", + " requests.storage: {5}\n", + " count/objectbucketclaims.objectbucket.io: {6} \n", + " \"\"\".format(NAMESPACE_NAME, CPU_LIMIT, CPU_REQUESTS, MEMORY_LIMIT, MEMORY_REQUEST, STORAGE, NUMBER_OF_BUCKETS))\n", + "\n", + " with open(custom_quota_path, 'w') as f:\n", + " yaml.dump(custom_quota, f)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We now include this Custom Resource along with our namespace build." + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/tmp/tmpjmgbmtb6/cluster-scope/base/core/namespaces/demo-projecx\n" + ] + } + ], + "source": [ + "%cd {WORKDIR}/cluster-scope/base/core/namespaces/{NAMESPACE_NAME}\n", + "!kustomize edit add resource resourcequota.yaml" + ] + }, + { + "cell_type": "markdown", "metadata": {}, + "source": [ + "### 6. Adding group to Operate-First clusters\n", + "\n", + "We have created the OCP group manifest, now let's ensure that it is deployed to all our clusters by updating our common `kustomization.yaml` that is deployed on all clusters." + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/tmp/tmpjmgbmtb6/cluster-scope/overlays/prod/common\n" + ] + } + ], + "source": [ + "%cd {WORKDIR}/cluster-scope/overlays/prod/common\n", + "!kustomize edit add resource ../../../base/user.openshift.io/groups/{TEAM_NAME}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Once again we sort the resources in this file to ensure human readability." + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": { + "tags": [] + }, "outputs": [], "source": [ - "!yq e -i \".users = {NAMESPACE_ADMINS}\" cluster-scope/base/user.openshift.io/groups/{TEAM_NAME}/group.yaml" + "kustomization_path = WORKDIR + \"/cluster-scope/overlays/prod/common/kustomization.yaml\"\n", + "\n", + "with open(kustomization_path, \"r\") as f:\n", + " kustomization = yaml.safe_load(f)\n", + " kustomization['resources'].sort()\n", + "with open(kustomization_path, 'w') as f:\n", + " yaml.dump(kustomization, f)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "### 7. Populate your OCP Group\n", + "\n", + "Let's now add all the users you specified earlier to the OpenShift group making them project admins for the namespace we just created." + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/tmp/tmpjmgbmtb6\n" + ] + } + ], + "source": [ + "%cd {WORKDIR}\n", + "!yq e -i \".users = {NAMESPACE_ADMINS}\" -P cluster-scope/base/user.openshift.io/groups/{TEAM_NAME}/group.yaml" ] }, { @@ -200,26 +482,61 @@ "metadata": {}, "source": [ "## Finalize\n", - "Please stage your changes and send them as a PR against the [operate-first/apps](https://github.com/operate-first/apps) repository. \n", - "\n", - ":::{note}\n", - "Make sure that following files/ have been modified/added:\n", - "- [x] `cluster-scope/base/core/namespaces/$NAMESPACE_NAME/kustomization.yaml`\n", - "- [x] `cluster-scope/base/core/namespaces/$NAMESPACE_NAME/namespace.yaml`\n", - "- [x] `cluster-scope/base/user.openshift.io/groups/$TEAM_NAME/group.yaml`\n", - "- [x] `cluster-scope/base/user.openshift.io/groups/$TEAM_NAME/kustomization.yaml`\n", - "- [x] `cluster-scope/components/namespace-admin-rolebinding/$TEAM_NAME/kustomization.yaml`\n", - "- [x] `cluster-scope/components/namespace-admin-rolebinding/$TEAM_NAME/rbac.yaml`\n", - "- [x] `cluster-scope/overlays/prod/$TARGET_CLUSTER/kustomization.yaml`\n", - ":::\n", "\n", - "Once the PR is merged, all the desired changes should be applied and the listed users should have admin access to the specified namespaces in the target cluster." + "Review your changes by running the following:" ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 67, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "On branch master\n", + "Your branch is up to date with 'origin/master'.\n", + "\n", + "Changes not staged for commit:\n", + " (use \"git add ...\" to update what will be committed)\n", + " (use \"git restore ...\" to discard changes in working directory)\n", + "\t\u001b[31mmodified: cluster-scope/overlays/prod/common/kustomization.yaml\u001b[m\n", + "\t\u001b[31mmodified: cluster-scope/overlays/prod/moc/smaug/kustomization.yaml\u001b[m\n", + "\n", + "Untracked files:\n", + " (use \"git add ...\" to include in what will be committed)\n", + "\t\u001b[31mcluster-scope/base/core/namespaces/demo-projecx/\u001b[m\n", + "\t\u001b[31mcluster-scope/base/user.openshift.io/groups/demo-teamx/\u001b[m\n", + "\t\u001b[31mcluster-scope/components/project-admin-rolebindings/demo-teamx/\u001b[m\n", + "\n", + "no changes added to commit (use \"git add\" and/or \"git commit -a\")\n" + ] + } + ], + "source": [ + "!git status" + ] + }, + { + "cell_type": "markdown", "metadata": {}, + "source": [ + "Now let's stage, commit, and push your changes to your GitHub account." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "collapsed": true, + "jupyter": { + "outputs_hidden": true + }, + "tags": [] + }, "outputs": [ { "name": "stdout", @@ -261,18 +578,19 @@ } ], "source": [ - "!git status\n", "!git add .\n", "!git commit -m \"feat(onboarding): Add team {TEAM_NAME}\"\n", "!git push" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": {}, - "outputs": [], - "source": [] + "source": [ + "Once pushed send a pull request against the [operate-first/apps](https://github.com/operate-first/apps) repository. \n", + "\n", + "Once the pull request is merged, all the desired changes will be applied by our ArgoCD instance and the listed users should have admin access to the specified namespaces in the target cluster." + ] } ], "metadata": { @@ -294,7 +612,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.12" + "version": "3.8.6" } }, "nbformat": 4,