This is an automated email from the ASF dual-hosted git repository. markusthoemmes pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-openwhisk.git
The following commit(s) were added to refs/heads/master by this push: new f8a1506 Enable test code coverage collection for containers. (#3685) f8a1506 is described below commit f8a1506b2edc5e61476e44b924962a567c283b22 Author: Chetan Mehrotra <chet...@apache.org> AuthorDate: Tue Jul 31 17:16:08 2018 +0530 Enable test code coverage collection for containers. (#3685) --- ansible/environments/local/group_vars/all | 2 ++ ansible/roles/controller/tasks/deploy.yml | 33 +++++++++++++++++++--- ansible/roles/invoker/tasks/deploy.yml | 23 ++++++++++++++- ansible/templates/whisk.properties.j2 | 1 + core/controller/.dockerignore | 3 +- core/controller/Dockerfile.cov | 15 ++++++++++ core/controller/build.gradle | 6 ++++ core/invoker/.dockerignore | 3 +- core/invoker/Dockerfile.cov | 15 ++++++++++ core/invoker/build.gradle | 5 ++++ gradle/docker.gradle | 42 +++++++++++++++++++++++++++ tests/build.gradle | 47 +++++++++++++++++++++++++++++++ tools/travis/distDocker.sh | 3 ++ tools/travis/runSystemTests.sh | 1 + 14 files changed, 192 insertions(+), 7 deletions(-) diff --git a/ansible/environments/local/group_vars/all b/ansible/environments/local/group_vars/all index 30464ef..65e0a5a 100755 --- a/ansible/environments/local/group_vars/all +++ b/ansible/environments/local/group_vars/all @@ -4,6 +4,8 @@ openwhisk_tmp_dir: "{{ lookup('env', 'OPENWHISK_TMP_DIR')|default('/tmp', true) }}" config_root_dir: "{{ openwhisk_tmp_dir }}/wskconf" whisk_logs_dir: "{{ openwhisk_tmp_dir }}/wsklogs" +coverage_enabled: "{{ lookup('env', 'GRADLE_COVERAGE') | default('false', true) | bool}}" +coverage_logs_dir: "{{ openwhisk_tmp_dir }}/wskcov" docker_registry: "" docker_dns: "" runtimes_bypass_pull_for_local_images: true diff --git a/ansible/roles/controller/tasks/deploy.yml b/ansible/roles/controller/tasks/deploy.yml index 11d7269..91d132d 100644 --- a/ansible/roles/controller/tasks/deploy.yml +++ b/ansible/roles/controller/tasks/deploy.yml @@ -244,6 +244,33 @@ set_fact: env: "{{ env | combine(controller.extraEnv) }}" +- name: populate volumes for controller + set_fact: + controller_volumes: + - "{{ whisk_logs_dir }}/{{ controller_name }}:/logs" + - "{{ controller.confdir }}/{{ controller_name }}:/conf" + +- name: check if coverage collection is enabled + set_fact: + coverage_enabled: false + when: coverage_enabled is undefined + +- name: ensure controller coverage directory is created with permissions + file: + path: "{{ coverage_logs_dir }}/controller/{{ item }}" + state: directory + mode: 0777 + with_items: + - controller + - common + become: "{{ logs.dir.become }}" + when: coverage_enabled + +- name: extend controller volume for coverage + set_fact: + controller_volumes: "{{ controller_volumes|default({}) + [coverage_logs_dir+'/controller:/coverage'] }}" + when: coverage_enabled + - name: include plugins include_tasks: "{{ item }}.yml" with_items: "{{ controller_plugins | default([]) }}" @@ -252,15 +279,13 @@ docker_container: name: "{{ controller_name }}" image: - "{{docker_registry~docker.image.prefix}}/controller:{{docker.image.tag}}" + "{{docker_registry~docker.image.prefix}}/controller:{{ 'cov' if (coverage_enabled) else docker.image.tag }}" state: started recreate: true restart_policy: "{{ docker.restart.policy }}" hostname: "{{ controller_name }}" env: "{{ env }}" - volumes: - - "{{ whisk_logs_dir }}/{{ controller_name }}:/logs" - - "{{ controller.confdir }}/{{ controller_name }}:/conf" + volumes: "{{ controller_volumes }}" ports: "{{ ports_to_expose }}" command: /bin/sh -c diff --git a/ansible/roles/invoker/tasks/deploy.yml b/ansible/roles/invoker/tasks/deploy.yml index 40b4b7c..0a1e793 100644 --- a/ansible/roles/invoker/tasks/deploy.yml +++ b/ansible/roles/invoker/tasks/deploy.yml @@ -252,6 +252,27 @@ volumes: "{{ volumes|default('') }},/usr/lib/x86_64-linux-gnu/libapparmor.so.1:/usr/lib/x86_64-linux-gnu/libapparmor.so.1" when: ansible_distribution == "Ubuntu" +- name: check if coverage collection is enabled + set_fact: + coverage_enabled: false + when: coverage_enabled is undefined + +- name: ensure invoker coverage directory is created with permissions + file: + path: "{{ coverage_logs_dir }}/invoker/{{ item }}" + state: directory + mode: 0777 + with_items: + - invoker + - common + become: "{{ logs.dir.become }}" + when: coverage_enabled + +- name: extend invoker volume for coverage + set_fact: + volumes: "{{ volumes|default('') }},{{ coverage_logs_dir }}/invoker:/coverage" + when: coverage_enabled + - name: start invoker using docker cli docker_container: userns_mode: "host" @@ -260,7 +281,7 @@ name: "{{ invoker_name }}" hostname: "{{ invoker_name }}" restart_policy: "{{ docker.restart.policy }}" - image: "{{ docker_registry }}{{ docker.image.prefix }}/invoker:{{ docker.image.tag }}" + image: "{{ docker_registry }}{{ docker.image.prefix }}/invoker:{{ 'cov' if (coverage_enabled) else docker.image.tag }}" state: started recreate: true env: "{{ env }}" diff --git a/ansible/templates/whisk.properties.j2 b/ansible/templates/whisk.properties.j2 index ce3f2db..c18e22a 100644 --- a/ansible/templates/whisk.properties.j2 +++ b/ansible/templates/whisk.properties.j2 @@ -7,6 +7,7 @@ testing.auth={{ openwhisk_home }}/ansible/files/auth.guest vcap.services.file= whisk.logs.dir={{ whisk_logs_dir }} +whisk.coverage.logs.dir={{ coverage_logs_dir | default('') }} environment.type={{ environmentInformation.type }} whisk.ssl.client.verification={{ nginx.ssl.verify_client }} whisk.ssl.cert={{ nginx.ssl.path }}/{{ nginx.ssl.cert }} diff --git a/core/controller/.dockerignore b/core/controller/.dockerignore index 802f440..d6a369a 100644 --- a/core/controller/.dockerignore +++ b/core/controller/.dockerignore @@ -1,3 +1,4 @@ * !init.sh -!build/distributions \ No newline at end of file +!build/distributions +!build/tmp/docker-coverage diff --git a/core/controller/Dockerfile.cov b/core/controller/Dockerfile.cov new file mode 100644 index 0000000..035ce29 --- /dev/null +++ b/core/controller/Dockerfile.cov @@ -0,0 +1,15 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements; and to You under the Apache License, Version 2.0. + +FROM controller + +ARG OW_ROOT_DIR + +RUN mkdir -p /coverage/common && \ + mkdir -p /coverage/controller && \ + mkdir -p "${OW_ROOT_DIR}/common/scala/build" && \ + mkdir -p "${OW_ROOT_DIR}/core/controller/build" && \ + ln -s /coverage/common "${OW_ROOT_DIR}/common/scala/build/scoverage" && \ + ln -s /coverage/controller "${OW_ROOT_DIR}/core/controller/build/scoverage" + +COPY build/tmp/docker-coverage /controller/ \ No newline at end of file diff --git a/core/controller/build.gradle b/core/controller/build.gradle index ca4f3b5..b7cfb8e 100644 --- a/core/controller/build.gradle +++ b/core/controller/build.gradle @@ -27,6 +27,12 @@ distDocker.dependsOn ':common:scala:distDocker', 'distTar' project.archivesBaseName = "openwhisk-controller" +ext.coverageJars = [ + "${buildDir}/libs/${project.archivesBaseName}-$version-scoverage.jar", + "${project(':common:scala').buildDir.absolutePath}/libs/openwhisk-common-$version-scoverage.jar" +] +distDockerCoverage.dependsOn ':common:scala:jarScoverage', 'jarScoverage' + repositories { mavenCentral() } diff --git a/core/invoker/.dockerignore b/core/invoker/.dockerignore index 802f440..d6a369a 100644 --- a/core/invoker/.dockerignore +++ b/core/invoker/.dockerignore @@ -1,3 +1,4 @@ * !init.sh -!build/distributions \ No newline at end of file +!build/distributions +!build/tmp/docker-coverage diff --git a/core/invoker/Dockerfile.cov b/core/invoker/Dockerfile.cov new file mode 100644 index 0000000..0fbf1ba --- /dev/null +++ b/core/invoker/Dockerfile.cov @@ -0,0 +1,15 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements; and to You under the Apache License, Version 2.0. + +FROM invoker + +ARG OW_ROOT_DIR + +RUN mkdir -p /coverage/common && \ + mkdir -p /coverage/invoker && \ + mkdir -p "${OW_ROOT_DIR}/common/scala/build" && \ + mkdir -p "${OW_ROOT_DIR}/core/invoker/build" && \ + ln -s /coverage/common "${OW_ROOT_DIR}/common/scala/build/scoverage" && \ + ln -s /coverage/invoker "${OW_ROOT_DIR}/core/invoker/build/scoverage" + +COPY build/tmp/docker-coverage /invoker/ \ No newline at end of file diff --git a/core/invoker/build.gradle b/core/invoker/build.gradle index 327dc78..6ead95b 100644 --- a/core/invoker/build.gradle +++ b/core/invoker/build.gradle @@ -26,6 +26,11 @@ apply from: '../../gradle/docker.gradle' distDocker.dependsOn ':common:scala:distDocker', 'distTar' project.archivesBaseName = "openwhisk-invoker" +ext.coverageJars = [ + "${buildDir}/libs/${project.archivesBaseName}-$version-scoverage.jar", + "${project(':common:scala').buildDir.absolutePath}/libs/openwhisk-common-$version-scoverage.jar" +] +distDockerCoverage.dependsOn ':common:scala:jarScoverage', 'jarScoverage' repositories { mavenCentral() diff --git a/gradle/docker.gradle b/gradle/docker.gradle index 6ad6850..1377f0e 100644 --- a/gradle/docker.gradle +++ b/gradle/docker.gradle @@ -67,6 +67,48 @@ task distDocker { println("Building '${dockerImageName}' took ${TimeCategory.minus(new Date(), start)}") } } + +task distDockerCoverage() { + doLast { + def start = new Date() + //Copy the scoverage runtime jars + copy {from configurations.scoverage - configurations.compile; into "build/tmp/docker-coverage/ext-lib"} + //Copy the scoverage prepared jars + coverageJars.each {jar -> + copy {from file(jar); into "build/tmp/docker-coverage/lib"; rename { it.replace('-scoverage', '')}} + } + + def buildArgs = [ + "OW_ROOT_DIR=${project.rootProject.projectDir.absolutePath}" + ] + def dockerImageNameOrig = dockerImageName + dockerImageName = "$dockerImageName-cov" + + //Use absolute paths for dockerFile and build directory + String dockerFileDir = project.buildscript.sourceFile.getParentFile().getAbsolutePath() + String dockerFile = "$dockerFileDir/Dockerfile.cov" + + def cmd = dockerBinary + prepareBuildArgs(buildArgs) + ['-f', dockerFile, '-t', dockerImageName, dockerFileDir] + retry(cmd, dockerRetries, dockerTimeout) + println("Building '${dockerImageName}' took ${TimeCategory.minus(new Date(), start)}") + + //Replace the original image with coverage one + project.ext.dockerTaggedImageName = dockerImagePrefix + '/' + dockerImageNameOrig + ':' + "cov" + } + finalizedBy('tagImage') +} + +def prepareBuildArgs(List buildArgs) { + def result = ['build'] + if(project.hasProperty('dockerBuildArgs')) { + buildArgs.addAll(dockerBuildArgs) + } + buildArgs.each {arg -> + result += ['--build-arg', arg] + } + result +} + task tagImage { doLast { def versionString = (dockerBinary + ['-v']).execute().text diff --git a/tests/build.gradle b/tests/build.gradle index 05561e7..e82c59d 100644 --- a/tests/build.gradle +++ b/tests/build.gradle @@ -198,17 +198,64 @@ gradle.projectsEvaluated { } } +task copyMeasurementFiles() { + doLast{ + Project common = project(":common:scala") + Project controller = project(":core:controller") + Project invoker = project(":core:invoker") + + Properties wskProps = loadWhiskProps() + String covLogsDir = wskProps.getProperty('whisk.coverage.logs.dir') + assert covLogsDir : "Did not find coverage logs property 'whisk.coverage.logs.dir' in whisk props" + + File covLogs = new File(covLogsDir) + + copyAndRenameMeasurementFile(covLogs, 'controller', "common", common) + copyAndRenameMeasurementFile(covLogs, 'controller', "controller", controller) + copyAndRenameMeasurementFile(covLogs, 'invoker', "common", common) + copyAndRenameMeasurementFile(covLogs, 'invoker', "invoker", invoker) + } +} + /** * Task to generate coverage xml report. Requires the * tests to be executed prior to its invocation */ task reportCoverage(type: ScoverageReport) { dependsOn([ + copyMeasurementFiles, ':common:scala:reportScoverage', ':core:controller:reportScoverage', ':core:invoker:reportScoverage', ':tools:admin:reportScoverage' ]) + +} + +/** + * Scoverage measurement files are named like scoverage.measurements.xxx. Where xxx is thread id. While + * consolidating the files between container run and normal test run we need to rename the files generated by + * container run so that the file name becomes unique + */ +def copyAndRenameMeasurementFile(File covLogDir, String containerName, String moduleName, Project dest){ + File dir = new File(new File(covLogDir, containerName), moduleName) + if (!dir.exists()) { + println "Coverage logs directory ${dir.absolutePath} does not exist. Skipping measurement file collection" + return + } + copy{ + from(dir) + into("${dest.buildDir.absolutePath}/scoverage") + rename {it+".$containerName-container"} + } +} + +def loadWhiskProps(){ + Properties p = new Properties() + file('../whisk.properties').withInputStream {is -> + p.load(is) + } + p } /** diff --git a/tools/travis/distDocker.sh b/tools/travis/distDocker.sh index 4415fc3..7f40d0b 100755 --- a/tools/travis/distDocker.sh +++ b/tools/travis/distDocker.sh @@ -27,4 +27,7 @@ ROOTDIR="$SCRIPTDIR/../.." cd $ROOTDIR TERM=dumb ./gradlew distDocker -PdockerImagePrefix=testing $GRADLE_PROJS_SKIP +TERM=dumb ./gradlew :core:controller:distDockerCoverage -PdockerImagePrefix=testing +TERM=dumb ./gradlew :core:invoker:distDockerCoverage -PdockerImagePrefix=testing + echo "Time taken for ${0##*/} is $SECONDS secs" diff --git a/tools/travis/runSystemTests.sh b/tools/travis/runSystemTests.sh index 7d1619b..f9c9ad3 100755 --- a/tools/travis/runSystemTests.sh +++ b/tools/travis/runSystemTests.sh @@ -25,6 +25,7 @@ ROOTDIR="$SCRIPTDIR/../.." cd $ROOTDIR/tools/travis export ORG_GRADLE_PROJECT_testSetName="REQUIRE_SYSTEM" +export GRADLE_COVERAGE=true ./setupPrereq.sh