From 7c5b7de37ef0b09e5ce2ac528bf8f7c47ef72786 Mon Sep 17 00:00:00 2001 From: kunming Date: Mon, 12 Nov 2018 20:52:34 -0800 Subject: [PATCH] automate cloud shell --- bootstrap/cmd/bootstrap/app/ksServer.go | 31 +++++++++- components/gcp-click-to-deploy/src/App.tsx | 4 +- .../gcp-click-to-deploy/src/DeployForm.tsx | 18 ++---- .../src/configs/package.json | 56 +++++++++++++++++++ deployment/gke/cloud_shell_templates/conn.md | 5 ++ deployment/gke/cloud_shell_templates/conn.sh | 22 ++++++++ 6 files changed, 119 insertions(+), 17 deletions(-) create mode 100644 components/gcp-click-to-deploy/src/configs/package.json create mode 100644 deployment/gke/cloud_shell_templates/conn.md create mode 100755 deployment/gke/cloud_shell_templates/conn.sh diff --git a/bootstrap/cmd/bootstrap/app/ksServer.go b/bootstrap/cmd/bootstrap/app/ksServer.go index 5a60c03a505..d0f73f23608 100644 --- a/bootstrap/cmd/bootstrap/app/ksServer.go +++ b/bootstrap/cmd/bootstrap/app/ksServer.go @@ -38,6 +38,7 @@ import ( "io/ioutil" "math/rand" "github.com/cenkalti/backoff" + "bytes" ) // The name of the prototype for Jupyter. @@ -45,6 +46,7 @@ const JupyterPrototype = "jupyterhub" // root dir of local cached VERSIONED REGISTRIES const CachedRegistries = "/opt/versioned_registries" +const CloudShellTemplatePath = "/opt/registries/kubeflow/deployment/gke/cloud_shell_templates" // key used for storing start time of a request to deploy in the request contexts const StartTime = "StartTime" @@ -52,6 +54,7 @@ const StartTime = "StartTime" const KubeflowRegName = "kubeflow" const KubeflowFolder = "ks_app" const DmFolder = "gcp_config" +const CloudShellFolder = "kf_util" // KsService defines an interface for working with ksonnet. type KsService interface { @@ -495,8 +498,9 @@ func (s *ksServer) CreateApp(ctx context.Context, request CreateRequest, dmDeplo } if dmDeploy != nil { - s.UpdateDmConfig(repoDir, request.Project, request.Name, kfVersion, dmDeploy) + UpdateDmConfig(repoDir, request.Project, request.Name, kfVersion, dmDeploy) } + UpdateCloudShellConfig(repoDir, request.Project, request.Name, kfVersion, request.Zone) err = s.SaveAppToRepo(request.Project, request.Email, repoDir) if err != nil { log.Errorf("There was a problem saving config to cloud repo; %v", err) @@ -834,7 +838,7 @@ func (s *ksServer) GetApp(project string, appName string, kfVersion string, toke // Save ks app config local changes to project source repo. // Not thread safe, be aware when call it. -func (s *ksServer) UpdateDmConfig(repoDir string, project string, appName string, kfVersion string, dmDeploy *deploymentmanager.Deployment) error { +func UpdateDmConfig(repoDir string, project string, appName string, kfVersion string, dmDeploy *deploymentmanager.Deployment) error { confDir := path.Join(repoDir, GetRepoName(project), kfVersion, appName, DmFolder) if err := os.RemoveAll(confDir); err != nil { return err @@ -853,6 +857,29 @@ func (s *ksServer) UpdateDmConfig(repoDir string, project string, appName string return nil } +// Save cloud shell config to project source repo. +func UpdateCloudShellConfig(repoDir string, project string, appName string, kfVersion string, zone string) error { + confDir := path.Join(repoDir, GetRepoName(project), kfVersion, appName, CloudShellFolder) + if err := os.RemoveAll(confDir); err != nil { + return err + } + if err := os.MkdirAll(confDir, os.ModePerm); err != nil { + return err + } + for _, filename := range([]string{"conn.sh", "conn.md"}) { + data, err := ioutil.ReadFile(path.Join(CloudShellTemplatePath, filename)) + if err != nil { + return err + } + data = bytes.Replace(data, []byte("project_id_placeholder"), []byte(project), -1) + data = bytes.Replace(data, []byte("zone_placeholder"), []byte(zone), -1) + data = bytes.Replace(data, []byte("deploy_name_placeholder"), []byte(appName), -1) + if err := ioutil.WriteFile(path.Join(confDir, filename), data, os.ModePerm); err != nil { + return err + } + } + return nil +} // Save ks app config local changes to project source repo. // Not thread safe, be aware when call it. diff --git a/components/gcp-click-to-deploy/src/App.tsx b/components/gcp-click-to-deploy/src/App.tsx index c34f1d2a623..59fd954a644 100644 --- a/components/gcp-click-to-deploy/src/App.tsx +++ b/components/gcp-click-to-deploy/src/App.tsx @@ -139,9 +139,7 @@ class App extends React.Component { diff --git a/components/gcp-click-to-deploy/src/DeployForm.tsx b/components/gcp-click-to-deploy/src/DeployForm.tsx index 833fb75caec..9a56dddce1c 100644 --- a/components/gcp-click-to-deploy/src/DeployForm.tsx +++ b/components/gcp-click-to-deploy/src/DeployForm.tsx @@ -293,13 +293,10 @@ export default class DeployForm extends React.Component { }); return; } - window.open('https://console.cloud.google.com/home/dashboard?cloudshell=true&project=' + this.state.project, '_blank'); - this.setState({ - dialogBody: 'gcloud container clusters get-credentials ' + this.state.deploymentName - + ' --zone ' + this.state.zone + ' --project ' + this.state.project + '; ' + - 'kubectl port-forward -n kubeflow $(kubectl get pods -n kubeflow --selector=service=ambassador -o jsonpath="{.items[0].metadata.name}") 8081:80', - dialogTitle: 'In Cloud Shell, run following command:', - }); + const cloudShellConfPath = this.state.kfverison + '/' + this.state.deploymentName + '/kf_util'; + const cloudShellUrl = 'https://cloud.google.com/console/cloudshell/open?shellonly=true&git_repo=https://source.developers.google.com/p/' + + this.state.project + '/r/' + this.state.project + '-kubeflow-config&working_dir=' + cloudShellConfPath + '&tutorial=conn.md'; + window.open(cloudShellUrl, '_blank'); } private async _iapAddress() { @@ -520,12 +517,9 @@ export default class DeployForm extends React.Component { } else if (r.operation!.status! && r.operation!.status === 'DONE') { const readyTime = new Date(); readyTime.setTime(readyTime.getTime() + (20 * 60 * 1000)); - this._appendLine('Deployment is done'); + this._appendLine('Deployment initialized, configuring environment'); if (this.state.clientId === '' || this.state.clientSecret === '') { - this._appendLine('(IAP skipped), cluster should be ready within 5 minutes. To connect to cluster, click cloud shell and run:'); - this._appendLine('gcloud container clusters get-credentials ' + this.state.deploymentName - + ' --zone ' + this.state.zone + ' --project ' + this.state.project); - this._appendLine('kubectl port-forward -n kubeflow $(kubectl get pods -n kubeflow --selector=service=ambassador -o jsonpath="{.items[0].metadata.name}") 8080:80'); + this._appendLine('(IAP skipped), cluster should be ready within 5 minutes. To connect to cluster, click cloud shell and follow instruction'); } else { this._appendLine('your kubeflow app url should be ready within 20 minutes (by ' + readyTime.toLocaleTimeString() + '): https://' diff --git a/components/gcp-click-to-deploy/src/configs/package.json b/components/gcp-click-to-deploy/src/configs/package.json new file mode 100644 index 00000000000..1185653ff1f --- /dev/null +++ b/components/gcp-click-to-deploy/src/configs/package.json @@ -0,0 +1,56 @@ +{ + "deployment" : { "id" : 806689925228672508, "name" : "gbus" }, + "matches" : [ ], + "project" : "kunming-dev4", + "requestId" : "576851b7-2f49-3a6a-9ac3-31a79e1cc24c", + "resource" : + { + "name" : "set-iam-policy-add", + "properties" : + { + "gcpIamPolicyPatch" : + { + "add" : [ + { "members" : [ "serviceAccount:447707085492@cloudservices.gserviceaccount.com" ], "role" : "roles/container.admin" }, + { "members" : [ "serviceAccount:gbus-user@kunming-dev4.iam.gserviceaccount.com" ], "role" : "roles/cloudbuild.builds.editor" }, + { "members" : [ "serviceAccount:gbus-user@kunming-dev4.iam.gserviceaccount.com" ], "role" : "roles/viewer" }, + { "members" : [ "serviceAccount:gbus-admin@kunming-dev4.iam.gserviceaccount.com", "serviceAccount:gbus-user@kunming-dev4.iam.gserviceaccount.com" ], "role" : "roles/source.admin" }, + { "members" : [ "serviceAccount:gbus-admin@kunming-dev4.iam.gserviceaccount.com" ], "role" : "roles/servicemanagement.admin" }, + { "members" : [ "serviceAccount:gbus-admin@kunming-dev4.iam.gserviceaccount.com" ], "role" : "roles/compute.networkAdmin" }, + { "members" : [ "serviceAccount:gbus-user@kunming-dev4.iam.gserviceaccount.com" ], "role" : "roles/storage.admin" }, + { "members" : [ "serviceAccount:gbus-user@kunming-dev4.iam.gserviceaccount.com" ], "role" : "roles/bigquery.admin" }, + { "members" : [ "serviceAccount:gbus-user@kunming-dev4.iam.gserviceaccount.com" ], "role" : "roles/dataflow.admin" }, + { "members" : [ "serviceAccount:gbus-vm@kunming-dev4.iam.gserviceaccount.com" ], "role" : "roles/logging.logWriter" }, + { "members" : [ "serviceAccount:gbus-vm@kunming-dev4.iam.gserviceaccount.com" ], "role" : "roles/monitoring.metricWriter" }, + { "members" : [ "serviceAccount:gbus-vm@kunming-dev4.iam.gserviceaccount.com" ], "role" : "roles/storage.objectViewer" }, + { "members" : [ { "user" : "kunming@google.com" } ], "role" : "roles/iap.httpsResourceAccessor" } + ], + }, + "policy" : + { + "bindings" : [ + { "members" : [ "serviceAccount:kubeflow-user@kunming-dev4.iam.gserviceaccount.com" ], "role" : "roles/bigquery.admin" }, + { "members" : [ "serviceAccount:kubeflow-user@kunming-dev4.iam.gserviceaccount.com" ], "role" : "roles/cloudbuild.builds.editor" }, + { "members" : [ "serviceAccount:kubeflow-admin@kunming-dev4.iam.gserviceaccount.com" ], "role" : "roles/compute.networkAdmin" }, + { "members" : [ "serviceAccount:service-447707085492@compute-system.iam.gserviceaccount.com" ], "role" : "roles/compute.serviceAgent" }, + { "members" : [ "serviceAccount:447707085492@cloudservices.gserviceaccount.com" ], "role" : "roles/container.admin" }, + { "members" : [ "serviceAccount:service-447707085492@container-engine-robot.iam.gserviceaccount.com" ], "role" : "roles/container.serviceAgent" }, + { "members" : [ "serviceAccount:kubeflow-user@kunming-dev4.iam.gserviceaccount.com" ], "role" : "roles/dataflow.admin" }, + { "members" : [ "serviceAccount:447707085492-compute@developer.gserviceaccount.com", "serviceAccount:447707085492@cloudservices.gserviceaccount.com", "serviceAccount:service-447707085492@containerregistry.iam.gserviceaccount.com" ], "role" : "roles/editor" }, + { "members" : [ "user:kunming@google.com" ], "role" : "roles/iap.httpsResourceAccessor" }, { "members" : [ "serviceAccount:kubeflow-vm@kunming-dev4.iam.gserviceaccount.com" ], "role" : "roles/logging.logWriter" }, + { "members" : [ "serviceAccount:kubeflow-vm@kunming-dev4.iam.gserviceaccount.com" ], "role" : "roles/monitoring.metricWriter" }, { "members" : [ "user:kunming@google.com" ], "role" : "roles/owner" }, + { "members" : [ "serviceAccount:447707085492@cloudservices.gserviceaccount.com" ], "role" : "roles/resourcemanager.projectIamAdmin" }, + { "members" : [ "serviceAccount:kubeflow-admin@kunming-dev4.iam.gserviceaccount.com" ], "role" : "roles/servicemanagement.admin" }, + { "members" : [ "serviceAccount:kubeflow-admin@kunming-dev4.iam.gserviceaccount.com", "serviceAccount:kubeflow-user@kunming-dev4.iam.gserviceaccount.com" ], "role" : "roles/source.admin" }, + { "members" : [ "serviceAccount:kubeflow-user@kunming-dev4.iam.gserviceaccount.com" ], "role" : "roles/storage.admin" }, + { "members" : [ "serviceAccount:kubeflow-vm@kunming-dev4.iam.gserviceaccount.com" ], "role" : "roles/storage.objectViewer" }, + { "members" : [ "serviceAccount:kubeflow-user@kunming-dev4.iam.gserviceaccount.com" ], "role" : "roles/viewer" } + ], + "etag" : "BwV0mmCfkYU=", + "version" : 1, + }, + "resource" : "kunming-dev4", + }, + "self" : { }, + }, +} diff --git a/deployment/gke/cloud_shell_templates/conn.md b/deployment/gke/cloud_shell_templates/conn.md new file mode 100644 index 00000000000..7f6f170e6e7 --- /dev/null +++ b/deployment/gke/cloud_shell_templates/conn.md @@ -0,0 +1,5 @@ +# To establish connection to your kubeflow cluster + +## Run following command in your cloud shell terminal + +**./conn.sh** diff --git a/deployment/gke/cloud_shell_templates/conn.sh b/deployment/gke/cloud_shell_templates/conn.sh new file mode 100755 index 00000000000..d8f0bd90b6e --- /dev/null +++ b/deployment/gke/cloud_shell_templates/conn.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +# conn.sh - establish connection to kubeflow cluster + +set -e # exit on error + +PROJECT_ID=project_id_placeholder +ZONE=zone_placeholder +DEPLOY_NAME=deploy_name_placeholder + +gcloud container clusters get-credentials ${DEPLOY_NAME} --zone ${ZONE} --project ${PROJECT_ID} + +echo "Checking load balancing resource" +for i in $(seq 1 10) +do kubectl get pods -n kubeflow --selector=service=ambassador --field-selector=status.phase=Running | grep -q 'Running' && break || sleep 10 +done + +POD_NAME=$(kubectl get pods -n kubeflow --selector=service=ambassador --field-selector=status.phase=Running -o jsonpath="{.items[0].metadata.name}") + +echo "Load balancing now ready" +echo "Your kubeflow service now accessible via: https://ssh.cloud.google.com/devshell/proxy?authuser=0&port=8081" +kubectl port-forward -n kubeflow ${POD_NAME} 8081:80