diff --git a/.gitignore b/.gitignore index b2cc2080f200171c6c92203b92eaaeed5d7e4fcf..9d75cda55731040c93b4d98a173da6c9ff4b2799 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,6 @@ bower_components/ .tmp/ coverage/ dist/ -kubernetes/ .tools/ ## IDE specific files diff --git a/.travis.yml b/.travis.yml index 0c8e1be05850d667d2ec5c7d9e3c834dc177b93f..cfee884a7439da2a6e22491c2cd39c79138c56a9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,15 +41,5 @@ before_script: - export DISPLAY=:99.0 - sh -e /etc/init.d/xvfb start - # Download etcd and make add it to PATH environment variable. - # Etcd is used to manage kubernetes cluster coordination and for state management. - - curl -fsSL https://github.com/coreos/etcd/releases/download/v2.2.1/etcd-v2.2.1-linux-amd64.tar.gz | tar xzf - - - ln -fns etcd-v2.2.1-linux-amd64 etcd - - export PATH=$PWD/etcd:$PATH - - # Pull golang image to build local kubernetes cluster. - # Local cluster has build process that runs in a Docker container. - # This simplifies initial set up and provides a very consistent build and test environment. - - docker pull golang:1.4 -script: ./node_modules/.bin/gulp check:create-cluster +script: ./node_modules/.bin/gulp check:local-cluster after_script: ./node_modules/.bin/gulp coverage-codecov-upload diff --git a/README.md b/README.md index 5451ef2385832b0a52d0fa9eef7a194b3093ed2d..d22663eebcd92f15dbfc31e697a31b906a579d13 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,6 @@ Make sure the following software is installed and added to the `$PATH` variable: * go (1.5+) * nodejs (4.2.2+) * npm (1.3+) -* etcd (2.2+) * java (7+) * gulp (3.9+) @@ -31,16 +30,17 @@ $ npm install ## Run a Kubernetes Cluster -For development it is recommended to run a local Kubernetes cluster. For your convenience, a task is provided that checks out the latest stable version, compiles and runs the script: `/hack/local-up-cluster.sh`. Open a separate tab in your terminal and run the following command: +For development it is recommended to run a local Kubernetes cluster. For your convenience, a task is provided that checks out the latest stable version, and runs it inside a Docker container. Open a separate tab in your terminal and run the following command: ``` $ gulp local-up-cluster ``` -Depending on your environment you might need to run the gulp task with sudo. Also, you might need to propagate the `$PATH` settings to the root user. + +This will build and start a lightweight local cluster, consisting of a master and a single node. All processes run locally, in Docker container. The local cluster should behave like a real cluster, however, plugins like heapster are not installed. To shut it down, type the following command that kills all running Docker containers: + ``` -$ sudo env "PATH=$PATH" gulp local-up-cluster +$ docker kill $(docker ps -aq) ``` -This will build and start a lightweight local cluster, consisting of a master and a single node. All processes run locally, without virtual machines. The local cluster should behave like a real cluster, however, plugins like heapster are not installed. Type Control-C to shut it down. From time to time you might want to use to a real Kubernetes cluster (e.g. GCE, Vagrant) instead of the local one. The most convenient way is to create a proxy. Run the following command instead of the gulp task from above: ``` diff --git a/build/check.js b/build/check.js index f6f17f40e8cefbfd7e433137bcf759d883f2e04e..9180bb7618abf727840043afd760c3583135dea2 100644 --- a/build/check.js +++ b/build/check.js @@ -19,7 +19,6 @@ import gulp from 'gulp'; import gulpClangFormat from 'gulp-clang-format'; import gulpEslint from 'gulp-eslint'; import gulpSassLint from 'gulp-sass-lint'; -import runSequence from 'run-sequence'; import path from 'path'; import conf from './conf'; @@ -39,13 +38,7 @@ gulp.task('check', ['lint', 'build', 'test', 'integration-test:prod']); * Checks the code quality of Dashboard. In addition a local kubernetes cluster is spawned. * NOTE: This is meant as an entry point for CI jobs. */ -gulp.task('check:create-cluster', function() { - runSequence('local-up-cluster', ['check'], function(err) { - if (err) { - gulp.start('kill-all'); - } - }); -}); +gulp.task('check:local-cluster', ['lint', 'build', 'test', 'local-cluster-integration-test:prod']); /** * Lints all projects code files. diff --git a/build/cluster.js b/build/cluster.js index f8ffe9e4691c8088b7f75817daea59294a834204..42b1cb80ddbea3f22b7cad00cd58398c1896679a 100644 --- a/build/cluster.js +++ b/build/cluster.js @@ -16,43 +16,16 @@ * @fileoverview Gulp tasks for kubernetes cluster management. */ import childProcess from 'child_process'; -import del from 'del'; import gulp from 'gulp'; -import chmod from 'gulp-chmod'; -import gulpFilter from 'gulp-filter'; -import git from 'gulp-git'; -import gunzip from 'gulp-gunzip'; -import untar from 'gulp-untar'; import gulpUtil from 'gulp-util'; -import pathExists from 'path-exists'; -import request from 'request'; -import source from 'vinyl-source-stream'; import conf from './conf'; -const kubernetesArchive = 'kubernetes.tar.gz'; -const kubernetesUrl = 'https://github.com/kubernetes/kubernetes.git'; -const stableVersion = 'v1.1.1'; -const tarballUrl = 'https://storage.googleapis.com/kubernetes-release/release'; -const upScript = `${conf.paths.kubernetes}/hack/local-up-cluster.sh`; - /** * The healthz URL of the cluster to check that it is running. */ const clusterHealthzUrl = `http://${conf.backend.apiServerHost}/healthz`; -/** - * Currently running cluster process object. Null if the cluster is not running. - * - * @type {?child.ChildProcess} - */ -let clusterProcess = null; - -/** - * @type {boolean} The variable is set when there is an error during cluster creation. - */ -let clusterSpawnFailure = null; - /** * A Number, representing the ID value of the timer that is set for function which periodically * checks if cluster is running. The null means that no timer is running. @@ -61,19 +34,6 @@ let clusterSpawnFailure = null; */ let isRunningSetIntervalHandler = null; -/** - * Checks if there was a failure during cluster creation. - * Produces an error in case there was one. - * @param {function(?Error=)} doneFn - callback for the error - */ -function checkForClusterFailure(doneFn) { - if (clusterSpawnFailure) { - clearTimeout(isRunningSetIntervalHandler); - isRunningSetIntervalHandler = null; - doneFn(new Error('There was an error during cluster creation. Aborting.')); - } -} - /** * Checks if cluster health check return correct status. * When custer is up and running then return 'ok'. @@ -88,18 +48,6 @@ function clusterHealthCheck(doneFn) { }); } -/** - * Executes controls command using kubectl. - * @param {string} command - * @param {function(?Error=)} doneFn - */ -function executeKubectlCommand(command, doneFn) { - childProcess.exec(`${conf.paths.kubernetes}/cluster/kubectl.sh ${command}`, function(err) { - if (err) return doneFn(new Error(err)); - doneFn(); - }); -} - /** * Creates cluster from scratch. * Downloads latest version of kubernetes from git repository. @@ -114,96 +62,19 @@ function executeKubectlCommand(command, doneFn) { gulp.task('local-up-cluster', ['spawn-cluster', 'wait-for-cluster']); /** - * Tears down a Kubernetes cluster. + * Spawns a local Kubernetes cluster running inside a Docker container.: */ -gulp.task('kill-cluster', function(doneFn) { - if (clusterProcess) { - clusterProcess.on('exit', function() { - clusterProcess = null; - doneFn(); - }); - clusterProcess.kill(); - } else { - doneFn(); - } -}); - -/** - * Clones kubernetes from git repository. Task skip if kubernetes directory exist. - */ -gulp.task('clone-kubernetes', function(doneFn) { - pathExists(conf.paths.kubernetes).then(function(exists) { - if (!exists) { - git.clone(kubernetesUrl, {args: conf.paths.kubernetes}, function(err) { - if (err) return doneFn(new Error(err)); - doneFn(); - }); - } else { - doneFn(); - } - }); -}); - -/** - * Checkouts kubernetes to latest stable version. - */ -gulp.task('checkout-kubernetes-version', ['clone-kubernetes'], function(doneFn) { - git.checkout(stableVersion, {cwd: conf.paths.kubernetes, quiet: true}, function(err) { - if (err) return doneFn(new Error(err)); - doneFn(); - }); -}); - -/** - * Checks if kubectl is already downloaded. - * If not downloads kubectl for all platforms from tarball. - */ -gulp.task('download-kubectl', function(doneFn) { - let filter = gulpFilter('**/platforms/**'); - pathExists(`${conf.paths.kubernetes}/platforms`).then(function(exists) { - if (!exists) { - request(`${tarballUrl}/${stableVersion}/${kubernetesArchive}`) - .pipe(source(`${kubernetesArchive}`)) - .pipe(gunzip()) - .pipe(untar()) - .pipe(filter) - .pipe(chmod(755)) - .pipe(gulp.dest(conf.paths.base)) - .on('end', function() { doneFn(); }); - } else { - doneFn(); +gulp.task('spawn-cluster', function(doneFn) { + childProcess.execFile(conf.paths.hyperkube, function(err, stdout, stderr) { + if (err) { + console.log(stdout); + console.error(stderr); + return doneFn(new Error(err)); } + return doneFn(); }); }); -/** - * Removes kubernetes before git clone command. - */ -gulp.task('clear-kubernetes', function() { return del(conf.paths.kubernetes); }); - -/** - * Spawns local-up-cluster.sh script. - */ -gulp.task( - 'spawn-cluster', - [ - 'checkout-kubernetes-version', - 'kubeconfig-set-cluster-local', - 'kubeconfig-set-context-local', - 'kubeconfig-use-context-local', - 'kill-cluster', - ], - function() { - clusterProcess = childProcess.spawn(upScript, {stdio: 'inherit'}); - - clusterProcess.on('exit', function(code) { - if (code !== 0) { - clusterSpawnFailure = code; - } - clusterProcess = null; - }); - }); - /** * Checks periodically if cluster is up and running. */ @@ -221,8 +92,6 @@ gulp.task('wait-for-cluster', function(doneFn) { } counter += 1; - checkForClusterFailure(doneFn); - // constantly query the cluster until it is properly running clusterHealthCheck(function(result) { if (result === 'ok') { @@ -234,32 +103,3 @@ gulp.task('wait-for-cluster', function(doneFn) { }); } }); - -/** - * Sets a cluster entry in kubeconfig. - * Configures kubernetes server for localhost. - */ -gulp.task( - 'kubeconfig-set-cluster-local', ['download-kubectl', 'checkout-kubernetes-version'], - function(doneFn) { - executeKubectlCommand( - `config set-cluster local --server=http://${conf.backend.apiServerHost}` + - ' --insecure-skip-tls-verify=true', - doneFn); - }); - -/** - * Sets a context entry in kubeconfig as local. - */ -gulp.task( - 'kubeconfig-set-context-local', ['download-kubectl', 'checkout-kubernetes-version'], - function(doneFn) { - executeKubectlCommand('config set-context local --cluster=local', doneFn); - }); - -/** - * Sets the current-context in a kubeconfig file - */ -gulp.task( - 'kubeconfig-use-context-local', ['download-kubectl', 'checkout-kubernetes-version'], - function(doneFn) { executeKubectlCommand('config use-context local', doneFn); }); diff --git a/build/conf.js b/build/conf.js index 2de4003f3b8dec2814e8a21edcf0d68e0a4629a4..dd9fb09b9cdf92c9a8bff9bf35e7dd64cc65398d 100644 --- a/build/conf.js +++ b/build/conf.js @@ -95,9 +95,9 @@ export default { frontendTest: path.join(basePath, 'src/test/frontend'), goTools: path.join(basePath, '.tools/go'), goWorkspace: path.join(basePath, '.go_workspace'), + hyperkube: path.join(basePath, 'build/hyperkube.sh'), integrationTest: path.join(basePath, 'src/test/integration'), karmaConf: path.join(basePath, 'build/karma.conf.js'), - kubernetes: path.join(basePath, 'kubernetes'), nodeModules: path.join(basePath, 'node_modules'), partials: path.join(basePath, '.tmp/partials'), prodTmp: path.join(basePath, '.tmp/prod'), diff --git a/build/hyperkube.sh b/build/hyperkube.sh new file mode 100755 index 0000000000000000000000000000000000000000..386e10e121dc906e412f31eec3ec0dc4397070ea --- /dev/null +++ b/build/hyperkube.sh @@ -0,0 +1,45 @@ +#!/bin/bash +# Copyright 2015 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Starts local hypercube cluster in a Docker container. +# Learn more at https://github.com/kubernetes/kubernetes/blob/master/docs/getting-started-guides/docker.md + +# Version of kubernetes to use. +K8S_VERSION="1.1.2" +# Port of the apiserver to serve on. +PORT=8080 + +docker run --net=host -d gcr.io/google_containers/etcd:2.0.12 \ + /usr/local/bin/etcd --addr=127.0.0.1:4001 --bind-addr=0.0.0.0:4001 --data-dir=/var/etcd/data + + +docker run \ + --volume=/:/rootfs:ro \ + --volume=/sys:/sys:ro \ + --volume=/dev:/dev \ + --volume=/var/lib/docker/:/var/lib/docker:ro \ + --volume=/var/lib/kubelet/:/var/lib/kubelet:rw \ + --volume=/var/run:/var/run:rw \ + --net=host \ + --pid=host \ + --privileged=true \ + -d \ + gcr.io/google_containers/hyperkube:v${K8S_VERSION} \ + /hyperkube kubelet --containerized --hostname-override="127.0.0.1" \ + --address="0.0.0.0" --api-servers=http://localhost:${PORT} \ + --config=/etc/kubernetes/manifests + +docker run -d --net=host --privileged gcr.io/google_containers/hyperkube:v${K8S_VERSION} \ + /hyperkube proxy --master=http://127.0.0.1:${PORT} --v=2 diff --git a/build/test.js b/build/test.js index 2a6d214ae5016f63446530c773a4414049f1fb18..ec4172dff58f543a9c4163eadb71a1a30ed010b2 100644 --- a/build/test.js +++ b/build/test.js @@ -55,14 +55,14 @@ function runProtractorTests(doneFn) { // Close browser sync server to prevent the process from hanging. browserSyncInstance.exit(); // Kill backend server and cluster, if running. - gulp.start('kill-all'); + gulp.start('kill-backend'); doneFn(err); }) .on('end', function() { // Close browser sync server to prevent the process from hanging. browserSyncInstance.exit(); // Kill backend server and cluster, if running. - gulp.start('kill-all'); + gulp.start('kill-backend'); doneFn(); }); } @@ -135,11 +135,13 @@ gulp.task('integration-test', ['serve:nowatch', 'webdriver-update'], runProtract gulp.task('integration-test:prod', ['serve:prod', 'webdriver-update'], runProtractorTests); /** - * Downloads and updates webdriver. Required to keep it up to date. + * Runs application integration tests. Uses production version of the application. */ -gulp.task('webdriver-update', gulpProtractor.webdriver_update); +gulp.task( + 'local-cluster-integration-test:prod', ['serve:prod', 'webdriver-update', 'local-up-cluster'], + runProtractorTests); /** - * Kills backend server and cluster, if running. + * Downloads and updates webdriver. Required to keep it up to date. */ -gulp.task('kill-all', ['kill-backend', 'kill-cluster']); +gulp.task('webdriver-update', gulpProtractor.webdriver_update); diff --git a/package.json b/package.json index fac5c0965548dd3b8fb544ba446b45cc32939876..f484efe30fc20cd65c26e3e5457003f48213c163 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,6 @@ "gulp-angular-templatecache": "~1.8.0", "gulp-autoprefixer": "~3.1.0", "gulp-browserify": "~0.5.1", - "gulp-chmod": "~1.3.0", "gulp-clang-format": "~1.0.23", "gulp-closure-compiler": "~0.4.0", "gulp-codecov.io": "~1.0.1", @@ -33,8 +32,6 @@ "gulp-eslint": "~1.1.1", "gulp-filter": "~3.0.1", "gulp-flatten": "~0.2.0", - "gulp-git": "~1.6.0", - "gulp-gunzip": "~0.0.3", "gulp-htmlmin": "^1.3.0", "gulp-inject": "~3.0.0", "gulp-minify-css": "^1.2.2", @@ -48,7 +45,6 @@ "gulp-size": "~2.0.0", "gulp-sourcemaps": "~1.6.0", "gulp-uglify": "~1.5.1", - "gulp-untar": "~0.0.4", "gulp-useref": "~3.0.4", "gulp-util": "~3.0.7", "gulp-watch": "~4.3.5", @@ -61,14 +57,10 @@ "karma-ng-html2js-preprocessor": "~0.2.0", "karma-sourcemap-loader": "~0.3.6", "lodash": "~3.10.1", - "path-exists": "~2.1.0", "proxy-middleware": "~0.15.0", "q": "~1.4.1", - "request": "~2.67.0", - "run-sequence": "~1.1.5", "semver": "~5.1.0", "uglify-save-license": "~0.4.1", - "vinyl-source-stream": "1.1.0", "webpack-stream": "~3.1.0", "wiredep": "~3.0.0", "wrench": "~1.5.8"