From 051f7a8ac8825f037b6a41e60c73161eff1462b9 Mon Sep 17 00:00:00 2001 From: Mike Cobbett <77053+techcobweb@users.noreply.github.com> Date: Wed, 16 Oct 2024 16:59:20 +0100 Subject: [PATCH 1/4] build locally script - detect secrets file should not have timestamp inside on linux builds Signed-off-by: Mike Cobbett <77053+techcobweb@users.noreply.github.com> --- build-locally.sh | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/build-locally.sh b/build-locally.sh index e6caf23f..71328250 100755 --- a/build-locally.sh +++ b/build-locally.sh @@ -231,8 +231,19 @@ function check_secrets { error "Not all secrets found have been audited" exit 1 fi - sed -i '' '/[ ]*"generated_at": ".*",/d' .secrets.baseline success "secrets audit complete" + + h2 "Removing the timestamp from the secrets baseline file so it doesn't always cause a git change." + mkdir -p temp + rc=$? + check_exit_code $rc "Failed to create a temporary folder" + cat .secrets.baseline | grep -v "generated_at" > temp/.secrets.baseline.temp + rc=$? + check_exit_code $rc "Failed to create a temporary file with no timestamp inside" + mv temp/.secrets.baseline.temp .secrets.baseline + rc=$? + check_exit_code $rc "Failed to overwrite the secrets baseline with one containing no timestamp inside." + success "secrets baseline timestamp content has been removed ok" } function update_release_yaml { From 7c54ab0f18e5d582248c5b19511942d7dfad92bc Mon Sep 17 00:00:00 2001 From: Mike Cobbett <77053+techcobweb@users.noreply.github.com> Date: Mon, 21 Oct 2024 12:11:06 +0100 Subject: [PATCH 2/4] etcd client upgraded, gson level upgraded Signed-off-by: Mike Cobbett <77053+techcobweb@users.noreply.github.com> --- calculate-transitive-dependencies.sh | 206 ++++++++++++++++++ .../dev.galasa.cps.etcd/bnd.bnd | 78 ++++--- .../dev.galasa.cps.etcd/build.gradle | 63 +++++- 3 files changed, 301 insertions(+), 46 deletions(-) create mode 100755 calculate-transitive-dependencies.sh diff --git a/calculate-transitive-dependencies.sh b/calculate-transitive-dependencies.sh new file mode 100755 index 00000000..f6b5a2a4 --- /dev/null +++ b/calculate-transitive-dependencies.sh @@ -0,0 +1,206 @@ +#! /usr/bin/env bash + +# +# Copyright contributors to the Galasa project +# +# SPDX-License-Identifier: EPL-2.0 +# +#----------------------------------------------------------------------------------------- +# +# Objectives: Calculate transient dependencies so we can include them in the bnd bundle +# +#----------------------------------------------------------------------------------------- + +# Where is this script executing from ? +BASEDIR=$(dirname "$0");pushd $BASEDIR 2>&1 >> /dev/null ;BASEDIR=$(pwd);popd 2>&1 >> /dev/null +# echo "Running from directory ${BASEDIR}" +export ORIGINAL_DIR=$(pwd) +cd "${BASEDIR}" + +cd "${BASEDIR}/.." +WORKSPACE_DIR=$(pwd) +cd "${BASEDIR}" + +#----------------------------------------------------------------------------------------- +# +# Set Colors +# +#----------------------------------------------------------------------------------------- +bold=$(tput bold) +underline=$(tput sgr 0 1) +reset=$(tput sgr0) +red=$(tput setaf 1) +green=$(tput setaf 76) +white=$(tput setaf 7) +tan=$(tput setaf 202) +blue=$(tput setaf 25) + +#----------------------------------------------------------------------------------------- +# +# Headers and Logging +# +#----------------------------------------------------------------------------------------- +underline() { printf "${underline}${bold}%s${reset}\n" "$@" ;} +h1() { printf "\n${underline}${bold}${blue}%s${reset}\n" "$@" ;} +h2() { printf "\n${underline}${bold}${white}%s${reset}\n" "$@" ;} +debug() { printf "${white}%s${reset}\n" "$@" ;} +info() { printf "${white}➜ %s${reset}\n" "$@" ;} +success() { printf "${green}✔ %s${reset}\n" "$@" ;} +error() { printf "${red}✖ %s${reset}\n" "$@" ;} +warn() { printf "${tan}➜ %s${reset}\n" "$@" ;} +bold() { printf "${bold}%s${reset}\n" "$@" ;} +note() { printf "\n${underline}${bold}${blue}Note:${reset} ${blue}%s${reset}\n" "$@" ;} + +#----------------------------------------------------------------------------------------- +# Functions +#----------------------------------------------------------------------------------------- +function usage { + info "Syntax: calculate-dependencies.sh [OPTIONS]" + cat << EOF +Options are: + + +Environment variables used: + + +EOF +} + + +function create_temp_project { + h2 "Creating a temporary project so we can calculate the dependencies it has" + mkdir -p ${BASEDIR}/temp + cat << EOF > ${BASEDIR}/temp/pom.xml + + 4.0.0 + + dev.galasa + dependency-finder + 1.0-SNAPSHOT + jar + + + + io.etcd + jetcd-core + 0.5.9 + + + + +EOF + success "OK" +} + + +function list_transient_dependencies_using_maven { + + cd ${BASEDIR}/temp + h2 "This might be good in the bnd file of the cps extension:" + mvn dependency:tree | sed "s/\[INFO\]//g" | grep -v "BUILD SUCCESS" | grep -v "\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-" | grep -v "Total time" | grep -v "Finished at" \ + | grep -v "Scanning for projects" | grep -v "Building dependency-finder" | grep -v "maven-dependency-plugin" | grep -v "dev.galasa:dependency-finder" \ + | grep -v "^[ \t]*$" | sed "s/^[|+ \t\\\-]*//" | sed "s/:compile.*//g" | sed "s/:runtime.*//g" \ + | cut -d ':' -f2,4 \ + | sed "s/:/-/g" | sed "s/$/.jar; lib:=true,\\\/" | sed "s/^/ /" \ + | sort | uniq \ + > $BASEDIR/temp/dependencies_maven.txt + + info "See $BASEDIR/temp/dependencies_maven.txt" + success "OK" + + + h2 "This might be good in the gradle file of the cps extension:" + mvn dependency:tree | sed "s/\[INFO\]//g" | grep -v "BUILD SUCCESS" | grep -v "\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-" | grep -v "Total time" | grep -v "Finished at" \ + | grep -v "Scanning for projects" | grep -v "Building dependency-finder" | grep -v "maven-dependency-plugin" | grep -v "dev.galasa:dependency-finder" \ + | grep -v "^[ \t]*$" | sed "s/^[|+ \t\\\-]*//" | sed "s/:compile.*//g" | sed "s/:runtime.*//g" \ + | sed "s/^/ implementation ('/" \ + | sed "s/$/')/" \ + | sed "s/:jar:/:/" \ + | sort | uniq \ + > $BASEDIR/temp/dependencies_maven_imports.txt + + info "See $BASEDIR/temp/dependencies_maven_imports.txt" + success "OK" + + + # mvn dependency:tree | sed "s/\[INFO\]//g" | grep -v "BUILD SUCCESS" | grep -v "\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-" | grep -v "Total time" | grep -v "Finished at" \ + # | grep -v "Scanning for projects" | grep -v "Building dependency-finder" | grep -v "maven-dependency-plugin" | grep -v "dev.galasa:dependency-finder" +} + + + +function list_transient_dependencies_using_gradle { + cd ${BASEDIR}/*parent + h2 "This might be good in the bnd file of the cps extension:" + gradle dev.galasa.cps.etcd:dependencies --configuration runtimeClasspath > $BASEDIR/temp/dependencies.txt + sed '/Indicates repeated occurrences/,$d' $BASEDIR/temp/dependencies.txt > $BASEDIR/temp/dependencies2.txt + cd ${BASEDIR}/temp + csplit $BASEDIR/temp/dependencies2.txt "/Runtime classpath of source set/" > /dev/null + mv $BASEDIR/temp/xx01 $BASEDIR/temp/dependencies3.txt + cat $BASEDIR/temp/dependencies3.txt | grep -v "runtimeClasspath - Runtime classpath of source set" \ + | sed "s/^[+ \\|\-]*//" \ + | cut -d ':' -f2,3 \ + | sed "s/:[a-zA-Z0-9.\\-]* -> /:/" \ + | sed "s/ [(][*][)]$//" \ + | sed "s/:/-/" \ + | grep -v "^[ \t]*$" \ + | sed "s/$/.jar/" \ + | sed "s/^/ /" \ + | sed "s/$/; lib:=true,\\\/" \ + | grep -v "dev.galasa" \ + | grep -v "bcel" \ + | grep -v "commons-io-.*.jar" \ + | grep -v "commons-lang3-.*.jar" \ + | grep -v "commons-logging-" \ + | grep -v "easymock-.*.jar" \ + | grep -v "log4j-api-.*.jar" \ + | grep -v "log4j-core-.*.jar" \ + | grep -v "log4j-slf4j-impl-.*.jar" \ + | grep -v "org.apache.felix.bundlerepository-.*.jar" \ + | grep -v "org.osgi" \ + | sort | uniq \ + > $BASEDIR/temp/dependencies_gradle.txt + # cat $BASEDIR/temp/dependencies_gradle.txt +# + rm $BASEDIR/temp/dependencies2.txt $BASEDIR/temp/dependencies3.txt + info "See $BASEDIR/temp/dependencies_gradle.txt" + success "OK" + + cd ${BASEDIR}/*parent + h2 "This might be good in the gradle file of the cps extension:" + gradle dev.galasa.cps.etcd:dependencies --configuration runtimeClasspath > $BASEDIR/temp/dependencies.txt + sed '/Indicates repeated occurrences/,$d' $BASEDIR/temp/dependencies.txt > $BASEDIR/temp/dependencies2.txt + cd ${BASEDIR}/temp + csplit $BASEDIR/temp/dependencies2.txt "/Runtime classpath of source set/" > /dev/null + mv $BASEDIR/temp/xx01 $BASEDIR/temp/dependencies3.txt + cat $BASEDIR/temp/dependencies3.txt | grep -v "runtimeClasspath - Runtime classpath of source set" \ + | sed "s/^[+ \\|\-]*//" \ + | sed "s/:[a-zA-Z0-9.\\-]* -> /:/" \ + | sed "s/ [(][*][)]$//" \ + | grep -v "dev.galasa" \ + | grep -v "bcel" \ + | grep -v "commons-io" \ + | grep -v "commons-lang3" \ + | grep -v "commons-logging" \ + | grep -v "easymock" \ + | grep -v "org.apache.felix.bundlerepository" \ + | grep -v "org.osgi" \ + | sed "s/^/ implementation('/" \ + | sed "s/$/')/" \ + | grep -v " implementation('')" \ + | sort | uniq \ + > $BASEDIR/temp/dependencies_gradle_imports.txt + + # | grep -v "log4j-api" \ + # | grep -v "log4j-core" \ + # | grep -v "log4j-slf4j-impl" \ + + info "See $BASEDIR/temp/dependencies_gradle_imports.txt" + success "OK" +} + +create_temp_project +# list_transient_dependencies_using_maven +list_transient_dependencies_using_gradle \ No newline at end of file diff --git a/galasa-extensions-parent/dev.galasa.cps.etcd/bnd.bnd b/galasa-extensions-parent/dev.galasa.cps.etcd/bnd.bnd index 84589f85..6d8da9ef 100644 --- a/galasa-extensions-parent/dev.galasa.cps.etcd/bnd.bnd +++ b/galasa-extensions-parent/dev.galasa.cps.etcd/bnd.bnd @@ -12,39 +12,47 @@ Import-Package: \ javax.security.cert Embed-Transitive: true Embed-Dependency: *;scope=compile|runtime --includeresource: gson-2.10.1.jar; lib:=true,\ - jetcd-core-0.5.9.jar; lib:=true,\ - jetcd-common-0.5.9.jar; lib:=true,\ - grpc-core-1.39.0.jar; lib:=true,\ - grpc-api-1.39.0.jar; lib:=true,\ - grpc-context-1.39.0.jar; lib:=true,\ - error_prone_annotations-2.5.1.jar; lib:=true,\ - jsr305-3.0.2.jar; lib:=true,\ - animal-sniffer-annotations-1.19.jar; lib:=true,\ - annotations-4.1.1.4.jar; lib:=true,\ - perfmark-api-0.23.0.jar; lib:=true,\ - grpc-netty-1.39.0.jar; lib:=true,\ - netty-codec-http2-4.1.86.Final.jar; lib:=true,\ - netty-common-4.1.86.Final.jar; lib:=true,\ - netty-buffer-4.1.86.Final.jar; lib:=true,\ - netty-transport-4.1.86.Final.jar; lib:=true,\ - netty-resolver-4.1.86.Final.jar; lib:=true,\ - netty-codec-4.1.86.Final.jar; lib:=true,\ - netty-handler-4.1.86.Final.jar; lib:=true,\ - netty-codec-http-4.1.86.Final.jar; lib:=true,\ - netty-handler-proxy-4.1.86.Final.jar; lib:=true,\ - netty-codec-socks-4.1.86.Final.jar; lib:=true,\ - grpc-protobuf-1.39.0.jar; lib:=true,\ - protobuf-java-3.17.2.jar; lib:=true,\ - guava-30.1.1-jre.jar; lib:=true,\ - failureaccess-1.0.1.jar; lib:=true,\ - checker-qual-3.8.0.jar; lib:=true,\ - j2objc-annotations-1.3.jar; lib:=true,\ - proto-google-common-protos-2.0.1.jar; lib:=true,\ - grpc-protobuf-lite-1.39.0.jar; lib:=true,\ - grpc-stub-1.39.0.jar; lib:=true,\ - grpc-grpclb-1.39.0.jar; lib:=true,\ - protobuf-java-util-3.17.2.jar; lib:=true,\ - slf4j-api-1.7.32.jar; lib:=true,\ - failsafe-2.4.1.jar; lib:=true +-includeresource: \ + checker-qual-3.42.0.jar; lib:=true,\ + failsafe-3.3.2.jar; lib:=true,\ + failureaccess-1.0.2.jar; lib:=true,\ + grpc-api-1.66.0.jar; lib:=true,\ + grpc-context-1.66.0.jar; lib:=true,\ + grpc-core-1.66.0.jar; lib:=true,\ + grpc-grpclb-1.66.0.jar; lib:=true,\ + grpc-netty-1.66.0.jar; lib:=true,\ + grpc-protobuf-1.66.0.jar; lib:=true,\ + grpc-protobuf-lite-1.66.0.jar; lib:=true,\ + grpc-stub-1.66.0.jar; lib:=true,\ + grpc-util-1.66.0.jar; lib:=true,\ + gson-2.10.1.jar; lib:=true,\ + guava-33.2.1-jre.jar; lib:=true,\ + jackson-core-2.16.1.jar; lib:=true,\ + javax.annotation-api-1.3.2.jar; lib:=true,\ + jetcd-api-0.8.3.jar; lib:=true,\ + jetcd-common-0.8.3.jar; lib:=true,\ + jetcd-core-0.8.3.jar; lib:=true,\ + jetcd-grpc-0.8.3.jar; lib:=true,\ + listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar; lib:=true,\ + netty-buffer-4.1.111.Final.jar; lib:=true,\ + netty-codec-4.1.111.Final.jar; lib:=true,\ + netty-codec-dns-4.1.111.Final.jar; lib:=true,\ + netty-codec-http-4.1.111.Final.jar; lib:=true,\ + netty-codec-http2-4.1.111.Final.jar; lib:=true,\ + netty-codec-socks-4.1.111.Final.jar; lib:=true,\ + netty-common-4.1.111.Final.jar; lib:=true,\ + netty-handler-4.1.111.Final.jar; lib:=true,\ + netty-handler-proxy-4.1.111.Final.jar; lib:=true,\ + netty-resolver-4.1.111.Final.jar; lib:=true,\ + netty-resolver-dns-4.1.111.Final.jar; lib:=true,\ + netty-transport-4.1.111.Final.jar; lib:=true,\ + netty-transport-native-unix-common-4.1.111.Final.jar; lib:=true,\ + perfmark-api-0.27.0.jar; lib:=true,\ + proto-google-common-protos-2.41.0.jar; lib:=true,\ + protobuf-java-3.25.3.jar; lib:=true,\ + protobuf-java-util-3.25.3.jar; lib:=true,\ + slf4j-api-2.0.16.jar; lib:=true,\ + validation-api-2.0.1.Final.jar; lib:=true,\ + vertx-core-4.5.9.jar; lib:=true,\ + vertx-grpc-4.5.9.jar; lib:=true -fixupmessages "Classes found in the wrong directory"; restrict:=error; is:=warning diff --git a/galasa-extensions-parent/dev.galasa.cps.etcd/build.gradle b/galasa-extensions-parent/dev.galasa.cps.etcd/build.gradle index 627a0b1c..4cf92f12 100644 --- a/galasa-extensions-parent/dev.galasa.cps.etcd/build.gradle +++ b/galasa-extensions-parent/dev.galasa.cps.etcd/build.gradle @@ -8,19 +8,60 @@ description = 'Galasa etcd3 for CPS, DSS and Credentials - Provides the CPS, DSS version = '0.38.0' dependencies { - implementation ('io.etcd:jetcd-core:0.5.9') + implementation ('io.etcd:jetcd-core:0.8.3') + // { + // exclude group: com.google.code.gson ,module: gson + // } + // We don't want to use their gson implementation. + implementation ('com.google.code.gson:gson:2.10.1') // Not required for compile, but required to force the download of the jars to embed by bnd - implementation ('com.google.code.gson:gson:2.10.1') - implementation ('org.codehaus.mojo:animal-sniffer-annotations:1.19') - implementation ('com.google.android:annotations:4.1.1.4') - implementation ('io.perfmark:perfmark-api:0.23.0') - implementation ('io.netty:netty-handler-proxy:4.1.86.Final') - implementation ('io.netty:netty-codec-socks:4.1.86.Final') - implementation ('io.netty:netty-codec-http2:4.1.86.Final') - implementation ('com.google.protobuf:protobuf-java-util:3.17.2') - implementation ('com.google.guava:guava:30.1.1-jre') - implementation ('com.google.guava:failureaccess:1.0.1') + implementation('com.fasterxml.jackson.core:jackson-core:2.16.1') + implementation('com.google.api.grpc:proto-google-common-protos:2.41.0') + implementation('com.google.code.gson:gson:2.10.1') + implementation('com.google.guava:failureaccess:1.0.2') + implementation('com.google.guava:guava:33.2.1-jre') + implementation('com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava') + implementation('com.google.protobuf:protobuf-java-util:3.25.3') + implementation('com.google.protobuf:protobuf-java:3.25.3') + implementation('dev.failsafe:failsafe:3.3.2') + implementation('io.etcd:jetcd-api:0.8.3') + implementation('io.etcd:jetcd-common:0.8.3') + implementation('io.etcd:jetcd-core:0.8.3') + implementation('io.etcd:jetcd-grpc:0.8.3') + implementation('io.grpc:grpc-api:1.66.0') + implementation('io.grpc:grpc-context:1.66.0') + implementation('io.grpc:grpc-core:1.66.0') + implementation('io.grpc:grpc-grpclb:1.66.0') + implementation('io.grpc:grpc-netty:1.66.0') + implementation('io.grpc:grpc-protobuf-lite:1.66.0') + implementation('io.grpc:grpc-protobuf:1.66.0') + implementation('io.grpc:grpc-stub:1.66.0') + implementation('io.grpc:grpc-util:1.66.0') + implementation('io.netty:netty-buffer:4.1.111.Final') + implementation('io.netty:netty-codec-dns:4.1.111.Final') + implementation('io.netty:netty-codec-http2:4.1.111.Final') + implementation('io.netty:netty-codec-http:4.1.111.Final') + implementation('io.netty:netty-codec-socks:4.1.111.Final') + implementation('io.netty:netty-codec:4.1.111.Final') + implementation('io.netty:netty-common:4.1.111.Final') + implementation('io.netty:netty-handler-proxy:4.1.111.Final') + implementation('io.netty:netty-handler:4.1.111.Final') + implementation('io.netty:netty-resolver-dns:4.1.111.Final') + implementation('io.netty:netty-resolver:4.1.111.Final') + implementation('io.netty:netty-transport-native-unix-common:4.1.111.Final') + implementation('io.netty:netty-transport:4.1.111.Final') + implementation('io.perfmark:perfmark-api:0.27.0') + implementation('io.vertx:vertx-core:4.5.9') + implementation('io.vertx:vertx-grpc:4.5.9') + implementation('javax.annotation:javax.annotation-api:1.3.2') + implementation('javax.validation:validation-api:2.0.1.Final') + // logging is already embedded inside the framework bundle.... + // implementation('org.apache.logging.log4j:log4j-api:2.17.1') + // implementation('org.apache.logging.log4j:log4j-core:2.17.1') + // implementation('org.apache.logging.log4j:log4j-slf4j-impl:2.17.1') + implementation('org.checkerframework:checker-qual:3.42.0') + implementation('org.slf4j:slf4j-api:2.0.16') } // Note: These values are consumed by the parent build process From 1d97c8bd5be206c051d59e7535562c1c7b2e377c Mon Sep 17 00:00:00 2001 From: Mike Cobbett <77053+techcobweb@users.noreply.github.com> Date: Mon, 21 Oct 2024 13:15:44 +0100 Subject: [PATCH 3/4] Added readme.md to explain how to uplift etcd transitive dependencies Signed-off-by: Mike Cobbett <77053+techcobweb@users.noreply.github.com> --- .../dev.galasa.cps.etcd/README.md | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 galasa-extensions-parent/dev.galasa.cps.etcd/README.md diff --git a/galasa-extensions-parent/dev.galasa.cps.etcd/README.md b/galasa-extensions-parent/dev.galasa.cps.etcd/README.md new file mode 100644 index 00000000..51f37c24 --- /dev/null +++ b/galasa-extensions-parent/dev.galasa.cps.etcd/README.md @@ -0,0 +1,29 @@ +# The cps etcd extension + +This extension provides access to an implementation of a CPS store based based on remotely connecting to an etcd server. + + +## How the packaging works + +This extension is perhaps slightly strange in that it doesn't every want to go out to maven central, so any dependencies it needs to run +must be included in the osgi bundle. + +The extension therefore needs to get hold of the transitive dependencies and include them in a 'fat jar' bundle. + +If you look at the built bundle (in the `galasa-extensions-parent/dev.galasa.cps.etcd/build/libs` folder) and unpack the jar, you will see that it packs up the dependndency jars in the root of the bundle jar. + +There are 2 parts to packaging this extension: +- The `build.gradle` file downloads everything that it needed to compile, but it also downloads all the transitive dependencies +- The `bnd.bnd` file which controls which jars are included in the 'fat jar'. + +The gradle plugin `biz.aQute.bnd.builder` is used to do the packing-up of the dependency jars, based on the content of the `bnd.bnd` file. + +## How to upgrade the dependencies + +- Remove the dependencies from the `build.gradle` file which are only there to make sure things are downloaded which can be bundled into the fat jar. +- Upgrade what you need to in the `build.gradle`. For example, bump the value for the `io.etcd:jetcd-core` bundle might be what you are trying to do. +- Run the `calculate-transitive-dependencies.sh` script (in the root of this module). This script calculates the transitive depdendencies of everything which is in the build.gradle file. That's why we need to clean the `build.gradle` to contain only the minimum to start with. +- Move the contents of `temp/dependencies_gradle_imports.txt` to the `build.gradle` file. +- Move the contents of `temp/dependencies_gradle.txt` into the `bnd.bnd` file. + +This should result in a long list of jars which are first downloaded, then packaged into the extension bundle jar. From 2aea7a19e18dd5513f6d5d6723b6649cbec262ef Mon Sep 17 00:00:00 2001 From: Eamonn Mansour <47121388+eamansour@users.noreply.github.com> Date: Mon, 21 Oct 2024 13:33:19 +0100 Subject: [PATCH 4/4] feat: Implement operations to set and delete credentials in etcd (#279) Signed-off-by: Eamonn Mansour <47121388+eamansour@users.noreply.github.com> --- .secrets.baseline | 13 +- .../src/main/groovy/galasa.extensions.gradle | 14 + .../dev.galasa.auth.couchdb/build.gradle | 9 - .../etcd/internal/Etcd3CredentialsStore.java | 113 +++---- .../galasa/cps/etcd/internal/Etcd3Store.java | 77 +++++ .../internal/Etcd3CredentialsStoreTest.java | 278 ++++++++++++++++++ .../internal/mocks/MockEncryptionService.java | 35 +++ .../etcd/internal/mocks/MockEtcdClient.java | 78 +++++ .../etcd/internal/mocks/MockEtcdKvClient.java | 120 ++++++++ .../dev.galasa.events.kafka/build.gradle | 9 - .../dev.galasa.ras.couchdb/build.gradle | 9 - 11 files changed, 672 insertions(+), 83 deletions(-) create mode 100644 galasa-extensions-parent/dev.galasa.cps.etcd/src/main/java/dev/galasa/cps/etcd/internal/Etcd3Store.java create mode 100644 galasa-extensions-parent/dev.galasa.cps.etcd/src/test/java/dev/galasa/etcd/internal/Etcd3CredentialsStoreTest.java create mode 100644 galasa-extensions-parent/dev.galasa.cps.etcd/src/test/java/dev/galasa/etcd/internal/mocks/MockEncryptionService.java create mode 100644 galasa-extensions-parent/dev.galasa.cps.etcd/src/test/java/dev/galasa/etcd/internal/mocks/MockEtcdClient.java create mode 100644 galasa-extensions-parent/dev.galasa.cps.etcd/src/test/java/dev/galasa/etcd/internal/mocks/MockEtcdKvClient.java diff --git a/.secrets.baseline b/.secrets.baseline index fafda2d6..e6b35c8d 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -75,7 +75,18 @@ "name": "TwilioKeyDetector" } ], - "results": {}, + "results": { + "galasa-extensions-parent/dev.galasa.cps.etcd/src/test/java/dev/galasa/etcd/internal/Etcd3CredentialsStoreTest.java": [ + { + "hashed_secret": "1beb7496ebbe82c61151be093956d83dac625c13", + "is_secret": false, + "is_verified": false, + "line_number": 246, + "type": "Secret Keyword", + "verified_result": null + } + ] + }, "version": "0.13.1+ibm.62.dss", "word_list": { "file": null, diff --git a/galasa-extensions-parent/buildSrc/src/main/groovy/galasa.extensions.gradle b/galasa-extensions-parent/buildSrc/src/main/groovy/galasa.extensions.gradle index 95aa7362..e33c4f64 100644 --- a/galasa-extensions-parent/buildSrc/src/main/groovy/galasa.extensions.gradle +++ b/galasa-extensions-parent/buildSrc/src/main/groovy/galasa.extensions.gradle @@ -1,6 +1,7 @@ plugins { id 'galasa.java' id 'biz.aQute.bnd.builder' + id 'jacoco' } dependencies { @@ -17,3 +18,16 @@ dependencies { testImplementation 'org.awaitility:awaitility:3.0.0' testImplementation 'org.assertj:assertj-core:3.16.1' } + +test { + finalizedBy jacocoTestReport +} + +jacocoTestReport { + dependsOn test + reports { + xml.required = true + csv.required = true + html.outputLocation = layout.buildDirectory.dir('jacocoHtml') + } +} \ No newline at end of file diff --git a/galasa-extensions-parent/dev.galasa.auth.couchdb/build.gradle b/galasa-extensions-parent/dev.galasa.auth.couchdb/build.gradle index 59d9ba6c..5936fbf2 100644 --- a/galasa-extensions-parent/dev.galasa.auth.couchdb/build.gradle +++ b/galasa-extensions-parent/dev.galasa.auth.couchdb/build.gradle @@ -1,7 +1,6 @@ plugins { id 'biz.aQute.bnd.builder' id 'galasa.extensions' - id 'jacoco' } description = 'Galasa Authentication - CouchDB' @@ -21,14 +20,6 @@ dependencies { testImplementation(project(':dev.galasa.extensions.mocks')) } -jacocoTestReport { - reports { - xml.required = true - csv.required = true - html.outputLocation = layout.buildDirectory.dir('jacocoHtml') - } -} - // Note: These values are consumed by the parent build process // They indicate which packages of functionality this OSGi bundle should be delivered inside, // or referenced from. diff --git a/galasa-extensions-parent/dev.galasa.cps.etcd/src/main/java/dev/galasa/cps/etcd/internal/Etcd3CredentialsStore.java b/galasa-extensions-parent/dev.galasa.cps.etcd/src/main/java/dev/galasa/cps/etcd/internal/Etcd3CredentialsStore.java index e8e98940..ab3d8544 100644 --- a/galasa-extensions-parent/dev.galasa.cps.etcd/src/main/java/dev/galasa/cps/etcd/internal/Etcd3CredentialsStore.java +++ b/galasa-extensions-parent/dev.galasa.cps.etcd/src/main/java/dev/galasa/cps/etcd/internal/Etcd3CredentialsStore.java @@ -5,14 +5,12 @@ */ package dev.galasa.cps.etcd.internal; -import static com.google.common.base.Charsets.UTF_8; - import java.io.UnsupportedEncodingException; import java.net.URI; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.util.List; -import java.util.concurrent.CompletableFuture; +import java.util.Properties; +import java.util.Map.Entry; import java.util.concurrent.ExecutionException; import javax.crypto.spec.SecretKeySpec; @@ -25,12 +23,10 @@ import dev.galasa.framework.spi.creds.CredentialsUsername; import dev.galasa.framework.spi.creds.CredentialsUsernamePassword; import dev.galasa.framework.spi.creds.CredentialsUsernameToken; +import dev.galasa.framework.spi.creds.FrameworkEncryptionService; import dev.galasa.framework.spi.creds.ICredentialsStore; -import io.etcd.jetcd.ByteSequence; +import dev.galasa.framework.spi.creds.IEncryptionService; import io.etcd.jetcd.Client; -import io.etcd.jetcd.KV; -import io.etcd.jetcd.KeyValue; -import io.etcd.jetcd.kv.GetResponse; /** * This class implements the credential store in a etcd store. Usernames, @@ -38,10 +34,9 @@ * * "secure.credentials.{SomeCredentialId};.username" */ -public class Etcd3CredentialsStore implements ICredentialsStore { - private final Client client; - private final KV kvClient; +public class Etcd3CredentialsStore extends Etcd3Store implements ICredentialsStore { private final SecretKeySpec key; + private final IEncryptionService encryptionService; /** * This constructor instantiates the Key value client that can retrieve values @@ -51,10 +46,8 @@ public class Etcd3CredentialsStore implements ICredentialsStore { * @throws CredentialsException A failure occurred. */ public Etcd3CredentialsStore(IFramework framework, URI etcd) throws CredentialsException { + super(etcd); try { - client = Client.builder().endpoints(etcd).build(); - kvClient = client.getKVClient(); - IConfigurationPropertyStoreService cpsService = framework.getConfigurationPropertyService("secure"); String encryptionKey = cpsService.getProperty("credentials.file", "encryption.key"); if (encryptionKey != null) { @@ -62,11 +55,19 @@ public Etcd3CredentialsStore(IFramework framework, URI etcd) throws CredentialsE } else { key = null; } + + this.encryptionService = new FrameworkEncryptionService(key); } catch (Exception e) { throw new CredentialsException("Unable to initialise the etcd credentials store", e); } } + public Etcd3CredentialsStore(SecretKeySpec key, IEncryptionService encryptionService, Client etcdClient) throws CredentialsException { + super(etcdClient); + this.key = key; + this.encryptionService = encryptionService; + } + /** * This method checks for the three available credential types in the * credentials stores and returns the appropiate response. @@ -78,50 +79,30 @@ public Etcd3CredentialsStore(IFramework framework, URI etcd) throws CredentialsE * @throws CredentialsException A failure occurred. */ public ICredentials getCredentials(String credentialsId) throws CredentialsException { - String token = get("secure.credentials." + credentialsId + ".token"); - if (token != null) { + try { + ICredentials credentials = null; + String token = get("secure.credentials." + credentialsId + ".token"); String username = get("secure.credentials." + credentialsId + ".username"); - if (username != null) { - return new CredentialsUsernameToken(key, username, token); + // Check if the credentials are UsernameToken or Token + if (token != null && username != null) { + credentials = new CredentialsUsernameToken(key, username, token); + } else if (token != null) { + credentials = new CredentialsToken(key, token); + } else if (username != null) { + // We have a username, so check if the credentials are UsernamePassword or Username + String password = get("secure.credentials." + credentialsId + ".password"); + if (password != null) { + credentials = new CredentialsUsernamePassword(key, username, password); + } else { + credentials = new CredentialsUsername(key, username); + } } - return new CredentialsToken(key, token); - } - - String username = get("secure.credentials." + credentialsId + ".username"); - String password = get("secure.credentials." + credentialsId + ".password"); - - if (username == null) { - return null; - } - - if (password == null) { - return new CredentialsUsername(key, username); - } - - return new CredentialsUsernamePassword(key, username, password); - } - - /** - * A get method which interacts with the etcd client correctly. - * - * @param key - the full key to search for with CredId and type of credential. - * @return String vaule response. - * @throws CredentialsException - */ - private String get(String key) throws CredentialsException { - ByteSequence bsKey = ByteSequence.from(key, UTF_8); - CompletableFuture getFuture = kvClient.get(bsKey); - try { - GetResponse response = getFuture.get(); - List kvs = response.getKvs(); - if (kvs.isEmpty()) { - return null; - } - return kvs.get(0).getValue().toString(UTF_8); + + return credentials; } catch (InterruptedException | ExecutionException e) { Thread.currentThread().interrupt(); - throw new CredentialsException("Could not retrieve key.", e); + throw new CredentialsException("Failed to get credentials", e); } } @@ -135,8 +116,30 @@ private static SecretKeySpec createKey(String secret) @Override public void shutdown() throws CredentialsException { - kvClient.close(); - client.close(); + super.shutdownStore(); } + @Override + public void setCredentials(String credentialsId, ICredentials credentials) throws CredentialsException { + Properties credentialProperties = credentials.toProperties(credentialsId); + + try { + for (Entry property : credentialProperties.entrySet()) { + put((String) property.getKey(), encryptionService.encrypt((String) property.getValue())); + } + } catch (InterruptedException | ExecutionException e) { + Thread.currentThread().interrupt(); + throw new CredentialsException("Failed to set credentials", e); + } + } + + @Override + public void deleteCredentials(String credentialsId) throws CredentialsException { + try { + deletePrefix("secure.credentials." + credentialsId); + } catch (InterruptedException | ExecutionException e) { + Thread.currentThread().interrupt(); + throw new CredentialsException("Failed to delete credentials", e); + } + } } diff --git a/galasa-extensions-parent/dev.galasa.cps.etcd/src/main/java/dev/galasa/cps/etcd/internal/Etcd3Store.java b/galasa-extensions-parent/dev.galasa.cps.etcd/src/main/java/dev/galasa/cps/etcd/internal/Etcd3Store.java new file mode 100644 index 00000000..a8856f95 --- /dev/null +++ b/galasa-extensions-parent/dev.galasa.cps.etcd/src/main/java/dev/galasa/cps/etcd/internal/Etcd3Store.java @@ -0,0 +1,77 @@ +/* + * Copyright contributors to the Galasa project + * + * SPDX-License-Identifier: EPL-2.0 + */ +package dev.galasa.cps.etcd.internal; + +import static java.nio.charset.StandardCharsets.UTF_8; + +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +import javax.validation.constraints.NotNull; + +import io.etcd.jetcd.ByteSequence; +import io.etcd.jetcd.Client; +import io.etcd.jetcd.KV; +import io.etcd.jetcd.KeyValue; +import io.etcd.jetcd.kv.GetResponse; +import io.etcd.jetcd.options.DeleteOption; + +/** + * Abstract class containing common methods used to interact with etcd, like getting, setting, + * and deleting properties. + */ +public abstract class Etcd3Store { + + protected final Client client; + protected final KV kvClient; + + public Etcd3Store(Client client) { + this.client = client; + this.kvClient = client.getKVClient(); + } + + public Etcd3Store(URI etcdUri) { + this(Client.builder().endpoints(etcdUri).build()); + } + + protected String get(String key) throws InterruptedException, ExecutionException { + ByteSequence bsKey = ByteSequence.from(key, UTF_8); + CompletableFuture getFuture = kvClient.get(bsKey); + GetResponse response = getFuture.get(); + List kvs = response.getKvs(); + + String retrievedKey = null; + if (!kvs.isEmpty()) { + retrievedKey = kvs.get(0).getValue().toString(UTF_8); + } + return retrievedKey; + } + + protected void put(String key, String value) throws InterruptedException, ExecutionException { + ByteSequence bytesKey = ByteSequence.from(key, UTF_8); + ByteSequence bytesValue = ByteSequence.from(value, UTF_8); + kvClient.put(bytesKey, bytesValue).get(); + } + + protected void delete(@NotNull String key) throws InterruptedException, ExecutionException { + ByteSequence bytesKey = ByteSequence.from(key, StandardCharsets.UTF_8); + kvClient.delete(bytesKey).get(); + } + + protected void deletePrefix(@NotNull String keyPrefix) throws InterruptedException, ExecutionException { + ByteSequence bsKey = ByteSequence.from(keyPrefix, UTF_8); + DeleteOption options = DeleteOption.newBuilder().isPrefix(true).build(); + kvClient.delete(bsKey, options).get(); + } + + protected void shutdownStore() { + kvClient.close(); + client.close(); + } +} diff --git a/galasa-extensions-parent/dev.galasa.cps.etcd/src/test/java/dev/galasa/etcd/internal/Etcd3CredentialsStoreTest.java b/galasa-extensions-parent/dev.galasa.cps.etcd/src/test/java/dev/galasa/etcd/internal/Etcd3CredentialsStoreTest.java new file mode 100644 index 00000000..fe519efd --- /dev/null +++ b/galasa-extensions-parent/dev.galasa.cps.etcd/src/test/java/dev/galasa/etcd/internal/Etcd3CredentialsStoreTest.java @@ -0,0 +1,278 @@ +/* + * Copyright contributors to the Galasa project + * + * SPDX-License-Identifier: EPL-2.0 + */ +package dev.galasa.etcd.internal; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Test; + +import dev.galasa.cps.etcd.internal.Etcd3CredentialsStore; +import dev.galasa.etcd.internal.mocks.MockEncryptionService; +import dev.galasa.etcd.internal.mocks.MockEtcdClient; +import dev.galasa.framework.spi.creds.CredentialsToken; +import dev.galasa.framework.spi.creds.CredentialsUsername; +import dev.galasa.framework.spi.creds.CredentialsUsernamePassword; +import dev.galasa.framework.spi.creds.CredentialsUsernameToken; + +import static org.assertj.core.api.Assertions.*; + +public class Etcd3CredentialsStoreTest { + + @Test + public void testGetUsernameCredentialsReturnsCredentialsOk() throws Exception { + // Given... + MockEncryptionService mockEncryptionService = new MockEncryptionService(); + String credsId = "CRED1"; + String username = "my-user"; + + Map mockCreds = new HashMap<>(); + mockCreds.put("secure.credentials." + credsId + ".username", username); + + MockEtcdClient mockClient = new MockEtcdClient(mockCreds); + Etcd3CredentialsStore store = new Etcd3CredentialsStore(null, mockEncryptionService, mockClient); + + // When... + CredentialsUsername creds = (CredentialsUsername) store.getCredentials(credsId); + + // Then... + assertThat(creds).isNotNull(); + assertThat(creds.getUsername()).isEqualTo(username); + } + + @Test + public void testGetUsernamePasswordCredentialsReturnsCredentialsOk() throws Exception { + // Given... + MockEncryptionService mockEncryptionService = new MockEncryptionService(); + String credsId = "CRED1"; + String username = "my-user"; + String password = "not-a-password"; + + Map mockCreds = new HashMap<>(); + mockCreds.put("secure.credentials." + credsId + ".username", username); + mockCreds.put("secure.credentials." + credsId + ".password", password); + + MockEtcdClient mockClient = new MockEtcdClient(mockCreds); + Etcd3CredentialsStore store = new Etcd3CredentialsStore(null, mockEncryptionService, mockClient); + + // When... + CredentialsUsernamePassword creds = (CredentialsUsernamePassword) store.getCredentials(credsId); + + // Then... + assertThat(creds).isNotNull(); + assertThat(creds.getUsername()).isEqualTo(username); + assertThat(creds.getPassword()).isEqualTo(password); + } + + @Test + public void testGetUsernameTokenCredentialsReturnsCredentialsOk() throws Exception { + // Given... + MockEncryptionService mockEncryptionService = new MockEncryptionService(); + String credsId = "CRED1"; + String username = "my-user"; + String token = "a-token"; + + Map mockCreds = new HashMap<>(); + mockCreds.put("secure.credentials." + credsId + ".username", username); + mockCreds.put("secure.credentials." + credsId + ".token", token); + + MockEtcdClient mockClient = new MockEtcdClient(mockCreds); + Etcd3CredentialsStore store = new Etcd3CredentialsStore(null, mockEncryptionService, mockClient); + + // When... + CredentialsUsernameToken creds = (CredentialsUsernameToken) store.getCredentials(credsId); + + // Then... + assertThat(creds).isNotNull(); + assertThat(creds.getUsername()).isEqualTo(username); + assertThat(creds.getToken()).isEqualTo(token.getBytes()); + } + + @Test + public void testGetTokenCredentialsReturnsCredentialsOk() throws Exception { + // Given... + MockEncryptionService mockEncryptionService = new MockEncryptionService(); + String credsId = "CRED1"; + String token = "a-token"; + + Map mockCreds = new HashMap<>(); + mockCreds.put("secure.credentials." + credsId + ".token", token); + + MockEtcdClient mockClient = new MockEtcdClient(mockCreds); + Etcd3CredentialsStore store = new Etcd3CredentialsStore(null, mockEncryptionService, mockClient); + + // When... + CredentialsToken creds = (CredentialsToken) store.getCredentials(credsId); + + // Then... + assertThat(creds).isNotNull(); + assertThat(creds.getToken()).isEqualTo(token.getBytes()); + } + + @Test + public void testSetUsernameCredentialsSetsCredentialsOk() throws Exception { + // Given... + MockEncryptionService mockEncryptionService = new MockEncryptionService(); + String credsId = "CRED1"; + String username = "a-username"; + + Map mockCreds = new HashMap<>(); + MockEtcdClient mockClient = new MockEtcdClient(mockCreds); + Etcd3CredentialsStore store = new Etcd3CredentialsStore(null, mockEncryptionService, mockClient); + + CredentialsUsername mockUsernameCreds = new CredentialsUsername(username); + + // When... + store.setCredentials(credsId, mockUsernameCreds); + + // Then... + assertThat(mockCreds).hasSize(1); + assertThat(mockCreds.get("secure.credentials." + credsId + ".username")).isEqualTo(username); + + // The credentials should have been encrypted when being set + assertThat(mockEncryptionService.getEncryptCount()).isEqualTo(1); + assertThat(mockEncryptionService.getDecryptCount()).isEqualTo(0); + } + + @Test + public void testSetUsernamePasswordCredentialsSetsCredentialsOk() throws Exception { + // Given... + MockEncryptionService mockEncryptionService = new MockEncryptionService(); + String credsId = "CRED1"; + String username = "a-username"; + String password = "not-a-password"; + + Map mockCreds = new HashMap<>(); + MockEtcdClient mockClient = new MockEtcdClient(mockCreds); + Etcd3CredentialsStore store = new Etcd3CredentialsStore(null, mockEncryptionService, mockClient); + + CredentialsUsernamePassword mockUsernamePasswordCreds = new CredentialsUsernamePassword(username, password); + + // When... + store.setCredentials(credsId, mockUsernamePasswordCreds); + + // Then... + assertThat(mockCreds).hasSize(2); + assertThat(mockCreds.get("secure.credentials." + credsId + ".username")).isEqualTo(username); + assertThat(mockCreds.get("secure.credentials." + credsId + ".password")).isEqualTo(password); + + // The credentials should have been encrypted when being set + assertThat(mockEncryptionService.getEncryptCount()).isEqualTo(2); + assertThat(mockEncryptionService.getDecryptCount()).isEqualTo(0); + } + + @Test + public void testSetUsernameTokenCredentialsSetsCredentialsOk() throws Exception { + // Given... + MockEncryptionService mockEncryptionService = new MockEncryptionService(); + String credsId = "CRED1"; + String username = "a-username"; + String token = "a-token"; + + Map mockCreds = new HashMap<>(); + MockEtcdClient mockClient = new MockEtcdClient(mockCreds); + Etcd3CredentialsStore store = new Etcd3CredentialsStore(null, mockEncryptionService, mockClient); + + CredentialsUsernameToken mockUsernameTokenCreds = new CredentialsUsernameToken(username, token); + + // When... + store.setCredentials(credsId, mockUsernameTokenCreds); + + // Then... + assertThat(mockCreds).hasSize(2); + assertThat(mockCreds.get("secure.credentials." + credsId + ".username")).isEqualTo(username); + assertThat(mockCreds.get("secure.credentials." + credsId + ".token")).isEqualTo(token); + + // The credentials should have been encrypted when being set + assertThat(mockEncryptionService.getEncryptCount()).isEqualTo(2); + assertThat(mockEncryptionService.getDecryptCount()).isEqualTo(0); + } + + @Test + public void testSetTokenCredentialsSetsCredentialsOk() throws Exception { + // Given... + MockEncryptionService mockEncryptionService = new MockEncryptionService(); + String credsId = "CRED1"; + String token = "a-token"; + + Map mockCreds = new HashMap<>(); + MockEtcdClient mockClient = new MockEtcdClient(mockCreds); + Etcd3CredentialsStore store = new Etcd3CredentialsStore(null, mockEncryptionService, mockClient); + + CredentialsToken mockTokenCreds = new CredentialsToken(token); + + // When... + store.setCredentials(credsId, mockTokenCreds); + + // Then... + assertThat(mockCreds).hasSize(1); + assertThat(mockCreds.get("secure.credentials." + credsId + ".token")).isEqualTo(token); + + // The credentials should have been encrypted when being set + assertThat(mockEncryptionService.getEncryptCount()).isEqualTo(1); + assertThat(mockEncryptionService.getDecryptCount()).isEqualTo(0); + } + + @Test + public void testDeleteCredentialsRemovesCredentialsOk() throws Exception { + // Given... + MockEncryptionService mockEncryptionService = new MockEncryptionService(); + String credsId = "CRED1"; + String username = "a-username"; + + Map mockCreds = new HashMap<>(); + mockCreds.put("secure.credentials." + credsId + ".username", username); + + MockEtcdClient mockClient = new MockEtcdClient(mockCreds); + Etcd3CredentialsStore store = new Etcd3CredentialsStore(null, mockEncryptionService, mockClient); + + // When... + assertThat(mockCreds).hasSize(1); + store.deleteCredentials(credsId); + + // Then... + assertThat(mockCreds).hasSize(0); + } + + @Test + public void testDeleteCredentialsRemovesCredentialsByPrefix() throws Exception { + // Given... + MockEncryptionService mockEncryptionService = new MockEncryptionService(); + String credsId = "CRED1"; + String username = "a-username"; + String password = "not-a-password"; + + Map mockCreds = new HashMap<>(); + mockCreds.put("secure.credentials." + credsId + ".username", username); + mockCreds.put("secure.credentials." + credsId + ".password", password); + + MockEtcdClient mockClient = new MockEtcdClient(mockCreds); + Etcd3CredentialsStore store = new Etcd3CredentialsStore(null, mockEncryptionService, mockClient); + + // When... + assertThat(mockCreds).hasSize(2); + store.deleteCredentials(credsId); + + // Then... + assertThat(mockCreds).hasSize(0); + } + + @Test + public void testShutdownClosesEtcdClientsOk() throws Exception { + // Given... + MockEncryptionService mockEncryptionService = new MockEncryptionService(); + Map mockCreds = new HashMap<>(); + + MockEtcdClient mockClient = new MockEtcdClient(mockCreds); + Etcd3CredentialsStore store = new Etcd3CredentialsStore(null, mockEncryptionService, mockClient); + + // When... + store.shutdown(); + + // Then... + assertThat(mockClient.isClientShutDown()).isTrue(); + } +} diff --git a/galasa-extensions-parent/dev.galasa.cps.etcd/src/test/java/dev/galasa/etcd/internal/mocks/MockEncryptionService.java b/galasa-extensions-parent/dev.galasa.cps.etcd/src/test/java/dev/galasa/etcd/internal/mocks/MockEncryptionService.java new file mode 100644 index 00000000..02090d27 --- /dev/null +++ b/galasa-extensions-parent/dev.galasa.cps.etcd/src/test/java/dev/galasa/etcd/internal/mocks/MockEncryptionService.java @@ -0,0 +1,35 @@ +/* + * Copyright contributors to the Galasa project + * + * SPDX-License-Identifier: EPL-2.0 + */ +package dev.galasa.etcd.internal.mocks; + +import dev.galasa.framework.spi.creds.CredentialsException; +import dev.galasa.framework.spi.creds.IEncryptionService; + +public class MockEncryptionService implements IEncryptionService { + + private int encryptCount = 0; + private int decryptCount = 0; + + public int getEncryptCount() { + return encryptCount; + } + + public int getDecryptCount() { + return decryptCount; + } + + @Override + public String encrypt(String plainText) throws CredentialsException { + encryptCount++; + return plainText; + } + + @Override + public String decrypt(String encryptedText) throws CredentialsException { + decryptCount++; + return encryptedText; + } +} diff --git a/galasa-extensions-parent/dev.galasa.cps.etcd/src/test/java/dev/galasa/etcd/internal/mocks/MockEtcdClient.java b/galasa-extensions-parent/dev.galasa.cps.etcd/src/test/java/dev/galasa/etcd/internal/mocks/MockEtcdClient.java new file mode 100644 index 00000000..92d67080 --- /dev/null +++ b/galasa-extensions-parent/dev.galasa.cps.etcd/src/test/java/dev/galasa/etcd/internal/mocks/MockEtcdClient.java @@ -0,0 +1,78 @@ +/* + * Copyright contributors to the Galasa project + * + * SPDX-License-Identifier: EPL-2.0 + */ +package dev.galasa.etcd.internal.mocks; + +import java.util.Map; + +import io.etcd.jetcd.Auth; +import io.etcd.jetcd.Client; +import io.etcd.jetcd.Cluster; +import io.etcd.jetcd.Election; +import io.etcd.jetcd.KV; +import io.etcd.jetcd.Lease; +import io.etcd.jetcd.Lock; +import io.etcd.jetcd.Maintenance; +import io.etcd.jetcd.Watch; + +public class MockEtcdClient implements Client { + + private KV kvClient; + private boolean isClientShutDown = false; + + public MockEtcdClient(Map kvContents) { + this.kvClient = new MockEtcdKvClient(kvContents); + } + + @Override + public KV getKVClient() { + return kvClient; + } + + @Override + public void close() { + isClientShutDown = true; + } + + public boolean isClientShutDown() { + return isClientShutDown; + } + + @Override + public Auth getAuthClient() { + throw new UnsupportedOperationException("Unimplemented method 'getAuthClient'"); + } + + @Override + public Cluster getClusterClient() { + throw new UnsupportedOperationException("Unimplemented method 'getClusterClient'"); + } + + @Override + public Election getElectionClient() { + throw new UnsupportedOperationException("Unimplemented method 'getElectionClient'"); + } + + @Override + public Lease getLeaseClient() { + throw new UnsupportedOperationException("Unimplemented method 'getLeaseClient'"); + } + + @Override + public Lock getLockClient() { + throw new UnsupportedOperationException("Unimplemented method 'getLockClient'"); + } + + @Override + public Maintenance getMaintenanceClient() { + throw new UnsupportedOperationException("Unimplemented method 'getMaintenanceClient'"); + } + + @Override + public Watch getWatchClient() { + throw new UnsupportedOperationException("Unimplemented method 'getWatchClient'"); + } + +} diff --git a/galasa-extensions-parent/dev.galasa.cps.etcd/src/test/java/dev/galasa/etcd/internal/mocks/MockEtcdKvClient.java b/galasa-extensions-parent/dev.galasa.cps.etcd/src/test/java/dev/galasa/etcd/internal/mocks/MockEtcdKvClient.java new file mode 100644 index 00000000..0062b3b0 --- /dev/null +++ b/galasa-extensions-parent/dev.galasa.cps.etcd/src/test/java/dev/galasa/etcd/internal/mocks/MockEtcdKvClient.java @@ -0,0 +1,120 @@ +/* + * Copyright contributors to the Galasa project + * + * SPDX-License-Identifier: EPL-2.0 + */ +package dev.galasa.etcd.internal.mocks; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; + +import com.google.protobuf.ByteString; + +import io.etcd.jetcd.ByteSequence; +import io.etcd.jetcd.KV; +import io.etcd.jetcd.Txn; +import io.etcd.jetcd.api.KeyValue; +import io.etcd.jetcd.api.KeyValue.Builder; +import io.etcd.jetcd.api.RangeResponse; +import io.etcd.jetcd.kv.CompactResponse; +import io.etcd.jetcd.kv.DeleteResponse; +import io.etcd.jetcd.kv.GetResponse; +import io.etcd.jetcd.kv.PutResponse; +import io.etcd.jetcd.options.CompactOption; +import io.etcd.jetcd.options.DeleteOption; +import io.etcd.jetcd.options.GetOption; +import io.etcd.jetcd.options.PutOption; + +public class MockEtcdKvClient implements KV { + + Map kvContents = new HashMap<>(); + + public MockEtcdKvClient(Map kvContents) { + this.kvContents = kvContents; + } + + @Override + public CompletableFuture get(ByteSequence key) { + String keyStr = key.toString(); + String value = kvContents.get(keyStr); + + RangeResponse rangeResponse; + if (value == null) { + rangeResponse = RangeResponse.newBuilder().build(); + } else { + ByteString keyByteStr = ByteString.copyFromUtf8(keyStr); + Builder builder = KeyValue.newBuilder().setKey(keyByteStr); + ByteString valueByteStr = ByteString.copyFromUtf8(value); + builder = builder.setValue(valueByteStr); + KeyValue kv = builder.build(); + rangeResponse = RangeResponse.newBuilder().addKvs(kv).build(); + } + GetResponse mockResponse = new GetResponse(rangeResponse, key); + return CompletableFuture.completedFuture(mockResponse); + } + + @Override + public CompletableFuture put(ByteSequence key, ByteSequence value) { + String keyStr = key.toString(); + String valueStr = value.toString(); + kvContents.put(keyStr, valueStr); + + return CompletableFuture.completedFuture(null); + } + + + @Override + public CompletableFuture delete(ByteSequence key, DeleteOption options) { + String keyStr = key.toString(); + + if (options.isPrefix()) { + Set keysToRemove = new HashSet<>(); + Set existingKeySet = kvContents.keySet(); + + for (String existingKey : existingKeySet) { + if (existingKey.startsWith(keyStr)) { + keysToRemove.add(existingKey); + } + } + + existingKeySet.removeAll(keysToRemove); + } else { + kvContents.remove(keyStr); + } + return CompletableFuture.completedFuture(null); + } + + @Override + public CompletableFuture compact(long key) { + throw new UnsupportedOperationException("Unimplemented method 'compact'"); + } + + @Override + public CompletableFuture compact(long key, CompactOption options) { + throw new UnsupportedOperationException("Unimplemented method 'compact'"); + } + + @Override + public CompletableFuture delete(ByteSequence key) { + throw new UnsupportedOperationException("Unimplemented method 'delete'"); + } + + @Override + public CompletableFuture get(ByteSequence key, GetOption options) { + throw new UnsupportedOperationException("Unimplemented method 'get'"); + } + + @Override + public CompletableFuture put(ByteSequence key, ByteSequence value, PutOption options) { + throw new UnsupportedOperationException("Unimplemented method 'put'"); + } + + @Override + public Txn txn() { + throw new UnsupportedOperationException("Unimplemented method 'txn'"); + } + +} diff --git a/galasa-extensions-parent/dev.galasa.events.kafka/build.gradle b/galasa-extensions-parent/dev.galasa.events.kafka/build.gradle index 7c5b5905..464b489f 100644 --- a/galasa-extensions-parent/dev.galasa.events.kafka/build.gradle +++ b/galasa-extensions-parent/dev.galasa.events.kafka/build.gradle @@ -1,7 +1,6 @@ plugins { id 'biz.aQute.bnd.builder' id 'galasa.extensions' - id 'jacoco' } description = 'Galasa Events Plug-In - Kafka' @@ -14,14 +13,6 @@ dependencies { testImplementation(project(':dev.galasa.extensions.mocks')) } -jacocoTestReport { - reports { - xml.required = true - csv.required = true - html.outputLocation = layout.buildDirectory.dir('jacocoHtml') - } -} - // Note: These values are consumed by the parent build process // They indicate which packages of functionality this OSGi bundle should be delivered inside, // or referenced from. diff --git a/galasa-extensions-parent/dev.galasa.ras.couchdb/build.gradle b/galasa-extensions-parent/dev.galasa.ras.couchdb/build.gradle index 60ab6881..ac505166 100644 --- a/galasa-extensions-parent/dev.galasa.ras.couchdb/build.gradle +++ b/galasa-extensions-parent/dev.galasa.ras.couchdb/build.gradle @@ -1,7 +1,6 @@ plugins { id 'biz.aQute.bnd.builder' id 'galasa.extensions' - id 'jacoco' } description = 'Galasa RAS - CouchDB' @@ -21,14 +20,6 @@ dependencies { testImplementation(project(':dev.galasa.extensions.mocks')) } -jacocoTestReport { - reports { - xml.required = true - csv.required = true - html.outputLocation = layout.buildDirectory.dir('jacocoHtml') - } -} - // Note: These values are consumed by the parent build process // They indicate which packages of functionality this OSGi bundle should be delivered inside, // or referenced from.