mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-11-15 06:49:56 +00:00
bug4811: interims commit
This commit is contained in:
1 parent
00cf53fba6
commit
a43dc46bb2
36 files changed
+683
-282
No files matched your search
@@ -36,84 +36,82 @@
|
||||
<booleanAttribute key="org.eclipse.jdt.debug.ui.INCLUDE_EXTERNAL_JARS" value="true"/>
|
||||
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_CLASSPATH_ONLY_JAR" value="false"/>
|
||||
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry containerPath="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8" javaProject="com.sap.sailing.gwt.ui" path="1" type="4"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.gwt.ui/src/main/resources" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.gwt.ui/src/main/java" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.googlecode.java-diff-utils/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.shared.android/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.common/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.common/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.datamining.annotations/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.security.common/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.shared.android/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/org.json.simple/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.geocoding/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.datamining.shared/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.declination/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.datamining.ui/src/main/resources" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.declination/resources" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.server/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.server/resources" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.expeditionconnector/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.udpconnector/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.server.gateway.serialization.shared.android/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.expeditionconnector.common/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.gwt/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.gwt/resources" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.filestorage/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.filestorage/resources" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.mongodb/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.replication/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.operationaltransformation/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.expeditionconnector.persistence/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.persistence/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.server.gateway.serialization/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.simulator/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.simulator/resources" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.tractracadapter/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.tractrac.clientmodule/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.security/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.security/resources" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.mail/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.swisstimingadapter/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.xrr.resultimport/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.resultimport/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.competitorimport/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.xrr.schema/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.swisstimingadapter.persistence/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.tractracadapter.persistence/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/org.moxieapps.gwt.highcharts/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.freg.resultimport/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.swisstimingreplayadapter/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.igtimiadapter/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.igtimiadapter.persistence/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.datamining.shared/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.datamining/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.datamining/resources" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.datamining/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.datamining/resources" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.datamining.provider/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.racelogtrackingadapter/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.racelogtrackingadapter/resources" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.manage2sail/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.manage2sail/resources" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.xrr.structureimport/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.security.ui/src/main/java" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.security.ui/src/main/resources" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.gwt.adminconsole/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.news/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.polars.datamining.shared/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.polars.datamining/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.polars.datamining/resources" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry id="org.eclipse.jdt.launching.classpathentry.defaultClasspath"> <memento exportedEntriesOnly="false" project="com.sap.sailing.gwt.ui"/> </runtimeClasspathEntry> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry path="3" projectName="com.sap.sse.common" type="1"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.datamining.ui/src/main/java" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.github.branflake2267.gwt-maps-api/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.google.gwt.ajaxloader/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.landscape.common/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.landscape.ui/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry containerPath="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8" javaProject="com.sap.sailing.gwt.ui" path="1" type="4"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.gwt.ui/src/main/resources" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.gwt.ui/src/main/java" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.googlecode.java-diff-utils/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.shared.android/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.common/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.common/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.datamining.annotations/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.security.common/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.shared.android/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/org.json.simple/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.geocoding/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.datamining.shared/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.declination/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.datamining.ui/src/main/resources" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.declination/resources" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.server/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.server/resources" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.expeditionconnector/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.udpconnector/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.server.gateway.serialization.shared.android/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.expeditionconnector.common/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.gwt/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.gwt/resources" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.filestorage/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.filestorage/resources" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.mongodb/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.replication/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.operationaltransformation/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.expeditionconnector.persistence/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.persistence/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.server.gateway.serialization/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.simulator/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.simulator/resources" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.tractracadapter/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.tractrac.clientmodule/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.security/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.security/resources" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.mail/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.swisstimingadapter/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.xrr.resultimport/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.resultimport/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.competitorimport/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.xrr.schema/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.swisstimingadapter.persistence/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.tractracadapter.persistence/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/org.moxieapps.gwt.highcharts/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.freg.resultimport/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.swisstimingreplayadapter/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.igtimiadapter/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.igtimiadapter.persistence/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.datamining.shared/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.datamining/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.datamining/resources" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.datamining/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.datamining/resources" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.datamining.provider/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.racelogtrackingadapter/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.racelogtrackingadapter/resources" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.manage2sail/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.manage2sail/resources" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.xrr.structureimport/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.security.ui/src/main/java" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.security.ui/src/main/resources" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.gwt.adminconsole/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.news/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.polars.datamining.shared/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.polars.datamining/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.polars.datamining/resources" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry id="org.eclipse.jdt.launching.classpathentry.defaultClasspath"> <memento exportedEntriesOnly="false" project="com.sap.sailing.gwt.ui"/> </runtimeClasspathEntry> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry path="3" projectName="com.sap.sse.common" type="1"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.datamining.ui/src/main/java" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.github.branflake2267.gwt-maps-api/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.google.gwt.ajaxloader/src" path="3" type="2"/> "/>
|
||||
</listAttribute>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="com.gwtplugins.gwt.eclipse.core.moduleClasspathProvider"/>
|
||||
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
|
||||
|
||||
+25
-13
@@ -51,6 +51,7 @@ import com.sap.sse.landscape.aws.orchestration.StartMongoDBServer;
|
||||
import com.sap.sse.landscape.mongodb.MongoEndpoint;
|
||||
import com.sap.sse.landscape.mongodb.MongoProcess;
|
||||
import com.sap.sse.landscape.mongodb.MongoReplicaSet;
|
||||
import com.sap.sse.landscape.ssh.SSHKeyPair;
|
||||
import com.sap.sse.landscape.ssh.SshCommandChannel;
|
||||
|
||||
import software.amazon.awssdk.regions.Region;
|
||||
@@ -78,8 +79,15 @@ public class TestProcedures {
|
||||
private String securityServiceReplicationBearerToken;
|
||||
private String mailSmtpPassword;
|
||||
|
||||
/**
|
||||
* Used for the symmetric encryption / decryption of private SSH keys. See also
|
||||
* {@link #getDecryptedPrivateKey(SSHKeyPair, byte[])}.
|
||||
*/
|
||||
private byte[] privateKeyEncryptionPassphrase;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
privateKeyEncryptionPassphrase = ("awptyf87l"+"097384sf;,57").getBytes();
|
||||
landscape = AwsLandscape.obtain();
|
||||
region = new AwsRegion(Region.EU_WEST_2);
|
||||
securityServiceReplicationBearerToken = System.getProperty(SECURITY_SERVICE_REPLICATION_BEARER_TOKEN);
|
||||
@@ -112,12 +120,13 @@ public class TestProcedures {
|
||||
@Test
|
||||
public void testStartupEmptyMultiServerAndDeployAnotherProcess() throws Exception {
|
||||
final String keyName = "MyKey-"+UUID.randomUUID();
|
||||
landscape.createKeyPair(region, keyName);
|
||||
landscape.createKeyPair(region, keyName, privateKeyEncryptionPassphrase);
|
||||
final StartMultiServer.Builder<?, String> builder = StartMultiServer.builder();
|
||||
final String sailingAnalyticsServerTag = "sailing-analytics-server";
|
||||
final StartMultiServer<String> startEmptyMultiServer = builder
|
||||
.setLandscape(landscape)
|
||||
.setKeyName(keyName)
|
||||
.setPrivateKeyEncryptionPassphrase(privateKeyEncryptionPassphrase)
|
||||
.setTags(Tags.with(sailingAnalyticsServerTag, ""))
|
||||
.setOptionalTimeout(optionalTimeout)
|
||||
.build();
|
||||
@@ -125,7 +134,7 @@ public class TestProcedures {
|
||||
// this is expected to have connected to the default "live" replica set.
|
||||
startEmptyMultiServer.run();
|
||||
final ApplicationProcessHost<String, SailingAnalyticsMetrics, SailingAnalyticsProcess<String>> host = startEmptyMultiServer.getHost();
|
||||
final SshCommandChannel sshChannel = host.createRootSshChannel(optionalTimeout);
|
||||
final SshCommandChannel sshChannel = host.createRootSshChannel(optionalTimeout, privateKeyEncryptionPassphrase);
|
||||
final String result = sshChannel.runCommandAndReturnStdoutAndLogStderr("ls "+ApplicationProcessHost.DEFAULT_SERVERS_PATH, /* stderr prefix */ null, /* stderr log level */ null);
|
||||
assertTrue(result.isEmpty());
|
||||
final HttpURLConnection connection = (HttpURLConnection) new URL("http", host.getPublicAddress().getCanonicalHostName(), 80, "").openConnection();
|
||||
@@ -140,10 +149,10 @@ public class TestProcedures {
|
||||
assertEquals(new HashSet<>(Arrays.asList(SailingAnalyticsApplicationConfiguration.Builder.DEFAULT_PORT, SailingAnalyticsApplicationConfiguration.Builder.DEFAULT_PORT+1)),
|
||||
new HashSet<>(Arrays.asList(processA.getPort(), processB.getPort())));
|
||||
assertEquals(new HashSet<>(Arrays.asList(SailingAnalyticsApplicationConfiguration.Builder.DEFAULT_TELNET_PORT, SailingAnalyticsApplicationConfiguration.Builder.DEFAULT_TELNET_PORT+1)),
|
||||
new HashSet<>(Arrays.asList(processA.getTelnetPortToOSGiConsole(optionalTimeout), processB.getTelnetPortToOSGiConsole(optionalTimeout))));
|
||||
new HashSet<>(Arrays.asList(processA.getTelnetPortToOSGiConsole(optionalTimeout, privateKeyEncryptionPassphrase), processB.getTelnetPortToOSGiConsole(optionalTimeout, privateKeyEncryptionPassphrase))));
|
||||
assertEquals(new HashSet<>(Arrays.asList(SailingAnalyticsApplicationConfiguration.Builder.DEFAULT_EXPEDITION_PORT, SailingAnalyticsApplicationConfiguration.Builder.DEFAULT_EXPEDITION_PORT+1)),
|
||||
new HashSet<>(Arrays.asList(processA.getExpeditionUdpPort(optionalTimeout), processB.getExpeditionUdpPort(optionalTimeout))));
|
||||
final SshCommandChannel curlChannel = host.createRootSshChannel(optionalTimeout);
|
||||
new HashSet<>(Arrays.asList(processA.getExpeditionUdpPort(optionalTimeout, privateKeyEncryptionPassphrase), processB.getExpeditionUdpPort(optionalTimeout, privateKeyEncryptionPassphrase))));
|
||||
final SshCommandChannel curlChannel = host.createRootSshChannel(optionalTimeout, privateKeyEncryptionPassphrase);
|
||||
final ByteArrayOutputStream stderr = new ByteArrayOutputStream();
|
||||
curlChannel.sendCommandLineSynchronously(
|
||||
"curl -k -i -H \"Host: b.sapsailing.com\" \"https://127.0.0.1\"", stderr);
|
||||
@@ -154,11 +163,11 @@ public class TestProcedures {
|
||||
final Iterable<ApplicationReplicaSet<String, SailingAnalyticsMetrics, SailingAnalyticsProcess<String>>> replicaSets =
|
||||
landscape.getApplicationReplicaSetsByTag(region, sailingAnalyticsServerTag, (theHost, dir)->{
|
||||
try {
|
||||
return new SailingAnalyticsProcessImpl<String>(theHost, dir, optionalTimeout);
|
||||
return new SailingAnalyticsProcessImpl<String>(theHost, dir, optionalTimeout, privateKeyEncryptionPassphrase);
|
||||
} catch (NumberFormatException | JSchException | IOException | InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}, optionalTimeout);
|
||||
}, optionalTimeout, privateKeyEncryptionPassphrase);
|
||||
// expecting to find at least "a" and "b"
|
||||
assertTrue(Util.containsAll(Util.map(replicaSets, ApplicationReplicaSet::getName), Arrays.asList(aServerName, bServerName)));
|
||||
Util.filter(replicaSets, rs->rs.getName().equals(aServerName) || rs.getName().equals(bServerName)).forEach(rs->assertTrue(Util.isEmpty(rs.getReplicas()) && rs.getMaster() != null));
|
||||
@@ -200,11 +209,12 @@ public class TestProcedures {
|
||||
@Test
|
||||
public void testAddMongoReplica() throws Exception {
|
||||
final String keyName = "MyKey-"+UUID.randomUUID();
|
||||
landscape.createKeyPair(region, keyName);
|
||||
landscape.createKeyPair(region, keyName, privateKeyEncryptionPassphrase);
|
||||
final StartMongoDBServer.Builder<?, String, SailingAnalyticsMetrics, SailingAnalyticsProcess<String>> builder = StartMongoDBServer.builder();
|
||||
final StartMongoDBServer<String, SailingAnalyticsMetrics, SailingAnalyticsProcess<String>> startMongoDBServerProcedure = builder
|
||||
.setLandscape(landscape)
|
||||
.setKeyName(keyName)
|
||||
.setPrivateKeyEncryptionPassphrase(privateKeyEncryptionPassphrase)
|
||||
.setOptionalTimeout(optionalTimeout)
|
||||
.build();
|
||||
try {
|
||||
@@ -228,7 +238,7 @@ public class TestProcedures {
|
||||
boolean fine = true;
|
||||
do {
|
||||
try {
|
||||
final SshCommandChannel sshChannel = mongoProcess.getHost().createSshChannel("ec2-user", optionalTimeout);
|
||||
final SshCommandChannel sshChannel = mongoProcess.getHost().createSshChannel("ec2-user", optionalTimeout, privateKeyEncryptionPassphrase);
|
||||
final String stdout = sshChannel.runCommandAndReturnStdoutAndLogStderr(
|
||||
"i=0; while [ $i -lt $(echo \"rs.status().members.length\" | mongo 2>/dev/null | tail -n +5 | head -n +1) ]; do echo \"rs.status().members[$i].stateStr\" | mongo 2>/dev/null | tail -n +5 | head -n +1; i=$((i+1)); done",
|
||||
"stderr while trying to fetch replica set members", Level.WARNING);
|
||||
@@ -245,13 +255,14 @@ public class TestProcedures {
|
||||
final String keyName = "MyKey-"+UUID.randomUUID();
|
||||
// Comment the following line to get default eu-west-2 test environment; uncomment to upgrade current production images in eu-west-1
|
||||
// final AwsRegion region = new AwsRegion(Region.EU_WEST_1);
|
||||
landscape.createKeyPair(region, keyName);
|
||||
landscape.createKeyPair(region, keyName, privateKeyEncryptionPassphrase);
|
||||
final com.sap.sailing.landscape.procedures.UpgradeAmi.Builder<?, String, SailingAnalyticsProcess<String>> imageUpgradeProcedureBuilder = UpgradeAmi.builder();
|
||||
final UpgradeAmi<String> imageUpgradeProcedure =
|
||||
imageUpgradeProcedureBuilder
|
||||
.setLandscape(landscape)
|
||||
.setRegion(region) // only required in case a non-default region is used; see Landscape.getDefaultRegion()
|
||||
.setKeyName(keyName)
|
||||
.setPrivateKeyEncryptionPassphrase(privateKeyEncryptionPassphrase)
|
||||
.setOptionalTimeout(optionalTimeout)
|
||||
.build();
|
||||
try {
|
||||
@@ -276,7 +287,7 @@ public class TestProcedures {
|
||||
void testConnectivity() throws Exception {
|
||||
final String serverName = "test"+new Random().nextInt();
|
||||
final String keyName = "MyKey-"+UUID.randomUUID();
|
||||
landscape.createKeyPair(region, keyName);
|
||||
landscape.createKeyPair(region, keyName, privateKeyEncryptionPassphrase);
|
||||
SailingAnalyticsMasterConfiguration.Builder<AppConfigBuilderT, String> applicationConfigurationBuilder = SailingAnalyticsMasterConfiguration.masterBuilder();
|
||||
applicationConfigurationBuilder
|
||||
.setServerName(serverName)
|
||||
@@ -292,6 +303,7 @@ public class TestProcedures {
|
||||
.setRegion(region)
|
||||
.setInstanceType(InstanceType.T3_LARGE)
|
||||
.setKeyName(keyName)
|
||||
.setPrivateKeyEncryptionPassphrase(privateKeyEncryptionPassphrase)
|
||||
.setTags(Tags.with("Hello", "World"))
|
||||
.setOptionalTimeout(optionalTimeout)
|
||||
.build();
|
||||
@@ -315,10 +327,10 @@ public class TestProcedures {
|
||||
// check env.sh access
|
||||
final SailingAnalyticsProcess<String> process = startSailingAnalyticsMaster.getSailingAnalyticsProcess();
|
||||
assertTrue(process.waitUntilReady(optionalTimeout));
|
||||
final String envSh = process.getEnvSh(optionalTimeout);
|
||||
final String envSh = process.getEnvSh(optionalTimeout, privateKeyEncryptionPassphrase);
|
||||
assertFalse(envSh.isEmpty());
|
||||
assertTrue("Couldn't find SERVER_NAME=\""+serverName+"\" in env.sh:\n"+envSh, envSh.contains("SERVER_NAME=\""+serverName+"\""));
|
||||
assertEquals(14888, process.getTelnetPortToOSGiConsole(optionalTimeout));
|
||||
assertEquals(14888, process.getTelnetPortToOSGiConsole(optionalTimeout, privateKeyEncryptionPassphrase));
|
||||
// Now create an ALB mapping, assuming to create the dynamic ALB:
|
||||
final String domain = "wiesen-weg.de";
|
||||
final String hostname = serverName+"."+domain;
|
||||
|
||||
-1
@@ -9,7 +9,6 @@ import com.sap.sailing.landscape.SailingReleaseRepository;
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
public class TestReleaseRepository {
|
||||
|
||||
@Test
|
||||
public void testForAtLeastOneRelease() {
|
||||
assertFalse(Util.isEmpty(SailingReleaseRepository.INSTANCE));
|
||||
|
||||
@@ -17,7 +17,8 @@ Require-Bundle: com.sap.sse.gwt,
|
||||
com.sap.sailing.landscape.common,
|
||||
com.sap.sailing.landscape,
|
||||
com.sap.sse.landscape,
|
||||
com.sap.sse.landscape.aws
|
||||
com.sap.sse.landscape.aws,
|
||||
com.amazon.aws.aws-java-api
|
||||
Export-Package: com.sap.sailing.landscape.ui.client,
|
||||
com.sap.sailing.landscape.ui.server,
|
||||
com.sap.sailing.landscape.ui.shared
|
||||
|
||||
+15
-5
@@ -14,6 +14,7 @@ import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.google.gwt.user.client.rpc.ServiceDefTarget;
|
||||
import com.google.gwt.user.client.ui.CaptionPanel;
|
||||
import com.google.gwt.user.client.ui.Grid;
|
||||
import com.google.gwt.user.client.ui.HorizontalPanel;
|
||||
import com.google.gwt.user.client.ui.Label;
|
||||
import com.google.gwt.user.client.ui.PasswordTextBox;
|
||||
import com.google.gwt.user.client.ui.TextBox;
|
||||
@@ -70,8 +71,10 @@ public class LandscapeManagementPanel extends VerticalPanel {
|
||||
public LandscapeManagementPanel(StringMessages stringMessages, UserService userService,
|
||||
AdminConsoleTableResources tableResources, ErrorReporter errorReporter) {
|
||||
landscapeManagementService = initAndRegisterLandscapeManagementService();
|
||||
final HorizontalPanel awsCredentialsAndSshKeys = new HorizontalPanel();
|
||||
add(awsCredentialsAndSshKeys);
|
||||
final CaptionPanel awsCredentialsPanel = new CaptionPanel(stringMessages.awsCredentials());
|
||||
add(awsCredentialsPanel);
|
||||
awsCredentialsAndSshKeys.add(awsCredentialsPanel);
|
||||
final Grid awsCredentialsGrid = new Grid(2, 2);
|
||||
awsCredentialsPanel.add(awsCredentialsGrid);
|
||||
awsCredentialsGrid.setWidget(0, 0, new Label(stringMessages.awsAccessKey()));
|
||||
@@ -101,6 +104,10 @@ public class LandscapeManagementPanel extends VerticalPanel {
|
||||
awsCredentialsGrid.setWidget(1, 0, new Label(stringMessages.awsSecret()));
|
||||
awsSecretPasswordTextBox = new PasswordTextBox();
|
||||
awsCredentialsGrid.setWidget(1, 1, awsSecretPasswordTextBox);
|
||||
final SshKeyManagementPanel sshKeyManagementPanel = new SshKeyManagementPanel(stringMessages, userService, landscapeManagementService, tableResources, errorReporter);
|
||||
final CaptionPanel sshKeysCaptionPanel = new CaptionPanel(stringMessages.sshKeys());
|
||||
awsCredentialsAndSshKeys.add(sshKeysCaptionPanel);
|
||||
sshKeysCaptionPanel.add(sshKeyManagementPanel);
|
||||
regionsTable = new TableWrapperWithSingleSelectionAndFilter<String, StringMessages, AdminConsoleTableResources>(
|
||||
stringMessages, errorReporter, /* enablePager */ false,
|
||||
/* entity identity comparator */ Optional.empty(), GWT.create(AdminConsoleTableResources.class),
|
||||
@@ -164,6 +171,8 @@ public class LandscapeManagementPanel extends VerticalPanel {
|
||||
add(mongoEndpointsBusy);
|
||||
regionsTable.getSelectionModel().addSelectionChangeHandler(e->
|
||||
{
|
||||
sshKeyManagementPanel.showKeysInRegion(awsAccessKeyTextBox.getValue(), awsSecretPasswordTextBox.getValue(),
|
||||
regionsTable.getSelectionModel().getSelectedObject());
|
||||
mongoEndpointsTable.getDataProvider().getList().clear();
|
||||
if (regionsTable.getSelectionModel().getSelectedObject() != null) {
|
||||
mongoEndpointsBusy.setBusy(true);
|
||||
@@ -185,16 +194,17 @@ public class LandscapeManagementPanel extends VerticalPanel {
|
||||
mongoEndpointsTable.getDataProvider().getList().clear();
|
||||
}
|
||||
});
|
||||
// TODO region could be a drop-down maybe
|
||||
// TODO upon region selection show MongoDB, RabbitMQ, AppServer and Central Reverse Proxy clusters, and upgradable AMIs in region
|
||||
// TODO support SSH key management: show existing keys, allow user to paste an encrypted private key for storage in MongoDB; allow user to upload public key and choose for launch and login procedures
|
||||
// TODO upon region selection show AppServer clusters, and upgradable AMIs in region
|
||||
// TODO try to identify archive servers
|
||||
// TODO support creating a new app server cluster
|
||||
// TODO support AMI upgrade
|
||||
// TODO support archiving and dismantling of an application server cluster
|
||||
// TODO support archive server upgrade
|
||||
// TODO support upgrading all app server instances in a region
|
||||
// TODO region could be a drop-down maybe, and it could remember its last selection
|
||||
// TODO upon region selection show RabbitMQ, and Central Reverse Proxy clusters in region
|
||||
// TODO support archiving and dismantling of an application server cluster
|
||||
// TODO support deploying a new app server process instance onto an existing app server host (multi-instance)
|
||||
// TODO support SSH key management: show existing keys, allow user to paste an encrypted private key for storage in MongoDB; allow user to upload public key and choose for launch and login procedures
|
||||
}
|
||||
|
||||
private LandscapeManagementWriteServiceAsync initAndRegisterLandscapeManagementService() {
|
||||
|
||||
+3
@@ -4,6 +4,7 @@ import java.util.ArrayList;
|
||||
|
||||
import com.google.gwt.user.client.rpc.RemoteService;
|
||||
import com.sap.sailing.landscape.ui.shared.MongoEndpointDTO;
|
||||
import com.sap.sailing.landscape.ui.shared.SSHKeyPairDTO;
|
||||
|
||||
public interface LandscapeManagementWriteService extends RemoteService {
|
||||
ArrayList<String> getRegions();
|
||||
@@ -11,4 +12,6 @@ public interface LandscapeManagementWriteService extends RemoteService {
|
||||
ArrayList<MongoEndpointDTO> getMongoEndpoints(String awsAccessKey, String awsSecret, String region);
|
||||
|
||||
MongoEndpointDTO getMongoEndpoint(String awsAccessKey, String awsSecret, String region, String replicaSetName);
|
||||
|
||||
ArrayList<SSHKeyPairDTO> getSshKeys(String awsAccessKey, String awsSecret, String regionId);
|
||||
}
|
||||
+8
-2
@@ -4,11 +4,17 @@ import java.util.ArrayList;
|
||||
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.sap.sailing.landscape.ui.shared.MongoEndpointDTO;
|
||||
import com.sap.sailing.landscape.ui.shared.SSHKeyPairDTO;
|
||||
|
||||
public interface LandscapeManagementWriteServiceAsync {
|
||||
void getRegions(AsyncCallback<ArrayList<String>> asyncCallback);
|
||||
|
||||
void getMongoEndpoints(String awsAccessKey, String awsSecret, String region, AsyncCallback<ArrayList<MongoEndpointDTO>> callback);
|
||||
void getMongoEndpoints(String awsAccessKey, String awsSecret, String regionId,
|
||||
AsyncCallback<ArrayList<MongoEndpointDTO>> callback);
|
||||
|
||||
void getMongoEndpoint(String awsAccessKey, String awsSecret, String region, String replicaSetName, AsyncCallback<MongoEndpointDTO> callback);
|
||||
void getMongoEndpoint(String awsAccessKey, String awsSecret, String region, String replicaSetName,
|
||||
AsyncCallback<MongoEndpointDTO> callback);
|
||||
|
||||
void getSshKeys(String awsAccessKey, String awsSecret, String regionId,
|
||||
AsyncCallback<ArrayList<SSHKeyPairDTO>> callback);
|
||||
}
|
||||
java/com.sap.sailing.landscape.ui/src/com/sap/sailing/landscape/ui/client/SshKeyManagementPanel.java
Executable
+81
@@ -0,0 +1,81 @@
|
||||
package com.sap.sailing.landscape.ui.client;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.google.gwt.user.client.ui.VerticalPanel;
|
||||
import com.sap.sailing.landscape.ui.client.i18n.StringMessages;
|
||||
import com.sap.sailing.landscape.ui.shared.SSHKeyPairDTO;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.gwt.adminconsole.AdminConsoleTableResources;
|
||||
import com.sap.sse.gwt.client.ErrorReporter;
|
||||
import com.sap.sse.gwt.client.celltable.EntityIdentityComparator;
|
||||
import com.sap.sse.gwt.client.celltable.TableWrapperWithSingleSelectionAndFilter;
|
||||
import com.sap.sse.gwt.client.controls.busyindicator.BusyIndicator;
|
||||
import com.sap.sse.gwt.client.controls.busyindicator.SimpleBusyIndicator;
|
||||
import com.sap.sse.security.ui.client.UserService;
|
||||
|
||||
public class SshKeyManagementPanel extends VerticalPanel {
|
||||
private final LandscapeManagementWriteServiceAsync landscapeManagementService;
|
||||
private final TableWrapperWithSingleSelectionAndFilter<SSHKeyPairDTO, StringMessages, AdminConsoleTableResources> sshKeyTable;
|
||||
private final BusyIndicator sshKeyLoadingBusy;
|
||||
private final ErrorReporter errorReporter;
|
||||
|
||||
public SshKeyManagementPanel(StringMessages stringMessages, UserService userService,
|
||||
LandscapeManagementWriteServiceAsync landscapeManagementService, AdminConsoleTableResources tableResources,
|
||||
ErrorReporter errorReporter) {
|
||||
this.landscapeManagementService = landscapeManagementService;
|
||||
this.errorReporter = errorReporter;
|
||||
sshKeyTable =
|
||||
new TableWrapperWithSingleSelectionAndFilter<SSHKeyPairDTO, StringMessages, AdminConsoleTableResources>(stringMessages, errorReporter, /* enablePager */ true,
|
||||
Optional.of(new EntityIdentityComparator<SSHKeyPairDTO>() {
|
||||
@Override
|
||||
public boolean representSameEntity(SSHKeyPairDTO dto1, SSHKeyPairDTO dto2) {
|
||||
return Util.equalsWithNull(dto1.getRegionId(), dto2.getRegionId()) &&
|
||||
Util.equalsWithNull(dto1.getName(), dto2.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode(SSHKeyPairDTO t) {
|
||||
return (t.getRegionId() == null ? 0 : t.getRegionId().hashCode()) ^
|
||||
(t.getName() == null ? 0 : t.getName().hashCode());
|
||||
}
|
||||
}), tableResources, Optional.empty(), /* filter label */ Optional.empty(), /* filter checkbox label */ null) {
|
||||
@Override
|
||||
protected Iterable<String> getSearchableStrings(SSHKeyPairDTO t) {
|
||||
return Arrays.asList(t.getName(), t.getRegionId(), t.getCreatorName());
|
||||
}
|
||||
};
|
||||
sshKeyTable.addColumn(object->object.getRegionId(), stringMessages.region());
|
||||
sshKeyTable.addColumn(object->object.getName(), stringMessages.name());
|
||||
sshKeyTable.addColumn(object->object.getCreatorName(), stringMessages.creator());
|
||||
sshKeyTable.addColumn(object->object.getCreationTime().toString(), stringMessages.creationTime());
|
||||
add(sshKeyTable);
|
||||
sshKeyLoadingBusy = new SimpleBusyIndicator();
|
||||
add(sshKeyLoadingBusy);
|
||||
}
|
||||
|
||||
public void showKeysInRegion(String awsAccessKey, String awsSecret, String regionId) {
|
||||
sshKeyTable.getDataProvider().getList().clear();
|
||||
if (regionId != null) {
|
||||
sshKeyLoadingBusy.setBusy(true);
|
||||
landscapeManagementService.getSshKeys(awsAccessKey, awsSecret, regionId, new AsyncCallback<ArrayList<SSHKeyPairDTO>>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError(caught.getMessage());
|
||||
sshKeyLoadingBusy.setBusy(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(ArrayList<SSHKeyPairDTO> sshKeyPairDTOs) {
|
||||
sshKeyTable.refresh(sshKeyPairDTOs);
|
||||
sshKeyLoadingBusy.setBusy(false);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
sshKeyTable.getDataProvider().getList().clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
@@ -17,4 +17,7 @@ com.sap.sse.gwt.adminconsole.StringMessages {
|
||||
String awsCredentials();
|
||||
String awsAccessKey();
|
||||
String awsSecret();
|
||||
String creator();
|
||||
String creationTime();
|
||||
String sshKeys();
|
||||
}
|
||||
+4
-1
@@ -5,4 +5,7 @@ upgrade=Upgrade
|
||||
replicaSet=Replica Set
|
||||
awsCredentials=AWS Credentials
|
||||
awsAccessKey=AWS Access Key
|
||||
awsSecret=AWS Secret
|
||||
awsSecret=AWS Secret
|
||||
creator=Creator
|
||||
creationTime=Creation Time
|
||||
sshKeys=SSH Keys
|
||||
+4
-1
@@ -5,4 +5,7 @@ upgrade=Aktualisieren
|
||||
replicaSet=Replica-Set
|
||||
awsCredentials=AWS Zugangsdaten
|
||||
awsAccessKey=AWS Zugangsschlüssel
|
||||
awsSecret=AWS Geheimnis
|
||||
awsSecret=AWS Geheimnis
|
||||
creator=Ersteller
|
||||
creationTime=Erstellungszeitpunkt
|
||||
sshKeys=SSH-Schlüssel
|
||||
+24
@@ -3,17 +3,25 @@ package com.sap.sailing.landscape.ui.server;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
|
||||
import com.sap.sailing.landscape.ui.client.LandscapeManagementWriteService;
|
||||
import com.sap.sailing.landscape.ui.shared.MongoEndpointDTO;
|
||||
import com.sap.sailing.landscape.ui.shared.SSHKeyPairDTO;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.Util.Pair;
|
||||
import com.sap.sse.gwt.server.ResultCachingProxiedRemoteServiceServlet;
|
||||
import com.sap.sse.landscape.application.ApplicationProcessMetrics;
|
||||
import com.sap.sse.landscape.aws.AwsLandscape;
|
||||
import com.sap.sse.landscape.aws.impl.AwsRegion;
|
||||
import com.sap.sse.landscape.mongodb.MongoEndpoint;
|
||||
import com.sap.sse.landscape.mongodb.MongoProcess;
|
||||
import com.sap.sse.landscape.mongodb.MongoProcessInReplicaSet;
|
||||
import com.sap.sse.landscape.mongodb.MongoReplicaSet;
|
||||
import com.sap.sse.landscape.ssh.SSHKeyPair;
|
||||
import com.sap.sse.security.shared.impl.SecuredSecurityTypes;
|
||||
|
||||
import software.amazon.awssdk.services.ec2.model.KeyPairInfo;
|
||||
|
||||
public class LandscapeManagementWriteServiceImpl extends ResultCachingProxiedRemoteServiceServlet
|
||||
implements LandscapeManagementWriteService {
|
||||
@@ -24,6 +32,7 @@ public class LandscapeManagementWriteServiceImpl extends ResultCachingProxiedRem
|
||||
|
||||
@Override
|
||||
public ArrayList<String> getRegions() {
|
||||
SecurityUtils.getSubject().checkPermission(SecuredSecurityTypes.LANDSCAPE.getStringPermission(SecuredSecurityTypes.LandscapeActions.MANAGE));
|
||||
final ArrayList<String> result = new ArrayList<>();
|
||||
Util.addAll(Util.map(AwsLandscape.obtain().getRegions(), r->r.getId()), result);
|
||||
return result;
|
||||
@@ -31,6 +40,7 @@ public class LandscapeManagementWriteServiceImpl extends ResultCachingProxiedRem
|
||||
|
||||
@Override
|
||||
public ArrayList<MongoEndpointDTO> getMongoEndpoints(String awsAccessKey, String awsSecret, String region) {
|
||||
SecurityUtils.getSubject().checkPermission(SecuredSecurityTypes.LANDSCAPE.getStringPermission(SecuredSecurityTypes.LandscapeActions.MANAGE));
|
||||
final ArrayList<MongoEndpointDTO> result = new ArrayList<>();
|
||||
for (final MongoEndpoint mongoEndpoint : AwsLandscape.obtain(awsAccessKey, awsSecret).getMongoEndpoints(new AwsRegion(region))) {
|
||||
final MongoEndpointDTO dto;
|
||||
@@ -54,4 +64,18 @@ public class LandscapeManagementWriteServiceImpl extends ResultCachingProxiedRem
|
||||
public MongoEndpointDTO getMongoEndpoint(String awsAccessKey, String awsSecret, String region, String replicaSetName) {
|
||||
return getMongoEndpoints(awsAccessKey, awsSecret, region).stream().filter(mep->Util.equalsWithNull(mep.getReplicaSetName(), replicaSetName)).findAny().orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayList<SSHKeyPairDTO> getSshKeys(String awsAccessKey, String awsSecret, String regionId) {
|
||||
final ArrayList<SSHKeyPairDTO> result = new ArrayList<>();
|
||||
final AwsLandscape<Object, ApplicationProcessMetrics, ?> landscape = AwsLandscape.obtain(awsAccessKey, awsSecret);
|
||||
final AwsRegion region = new AwsRegion(regionId);
|
||||
for (final KeyPairInfo keyPairInfo : landscape.getAllKeyPairInfos(region)) {
|
||||
final SSHKeyPair key = landscape.getSSHKeyPair(region, keyPairInfo.keyName());
|
||||
if (key != null) {
|
||||
result.add(new SSHKeyPairDTO(key.getRegionId(), key.getName(), key.getCreatorName(), key.getCreationTime()));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
Executable
+40
@@ -0,0 +1,40 @@
|
||||
package com.sap.sailing.landscape.ui.shared;
|
||||
|
||||
import com.google.gwt.user.client.rpc.IsSerializable;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
|
||||
public class SSHKeyPairDTO implements IsSerializable {
|
||||
private String regionId;
|
||||
private String name;
|
||||
private String creatorName;
|
||||
private TimePoint creationTime;
|
||||
|
||||
@Deprecated
|
||||
SSHKeyPairDTO() {
|
||||
// for GWT RPC serialization only
|
||||
}
|
||||
|
||||
public SSHKeyPairDTO(String regionId, String name, String creatorName, TimePoint creationTime) {
|
||||
super();
|
||||
this.regionId = regionId;
|
||||
this.name = name;
|
||||
this.creatorName = creatorName;
|
||||
this.creationTime = creationTime;
|
||||
}
|
||||
|
||||
public String getRegionId() {
|
||||
return regionId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getCreatorName() {
|
||||
return creatorName;
|
||||
}
|
||||
|
||||
public TimePoint getCreationTime() {
|
||||
return creationTime;
|
||||
}
|
||||
}
|
||||
+4
-2
@@ -13,7 +13,9 @@ public interface SailingAnalyticsProcess<ShardingKey> extends ApplicationProcess
|
||||
static Logger logger = Logger.getLogger(SailingAnalyticsProcess.class.getName());
|
||||
static String HEALTH_CHECK_PATH = "/gwt/status";
|
||||
|
||||
default int getExpeditionUdpPort(Optional<Duration> optionalTimeout) throws NumberFormatException, JSchException, IOException, InterruptedException, SftpException {
|
||||
return Integer.parseInt(getEnvShValueFor(SailingAnalyticsProcessConfigurationVariable.EXPEDITION_PORT.name(), optionalTimeout));
|
||||
default int getExpeditionUdpPort(Optional<Duration> optionalTimeout, byte[] privateKeyEncryptionPassphrase)
|
||||
throws NumberFormatException, JSchException, IOException, InterruptedException, SftpException {
|
||||
return Integer.parseInt(getEnvShValueFor(SailingAnalyticsProcessConfigurationVariable.EXPEDITION_PORT.name(),
|
||||
optionalTimeout, privateKeyEncryptionPassphrase));
|
||||
}
|
||||
}
|
||||
+4
-3
@@ -32,9 +32,10 @@ implements SailingAnalyticsProcess<ShardingKey> {
|
||||
/**
|
||||
* Tries to obtain the port from the {@code env.sh} file found in the {@code serverDirectory}
|
||||
*/
|
||||
public SailingAnalyticsProcessImpl(Host host, String serverDirectory, Optional<Duration> optionalTimeout)
|
||||
public SailingAnalyticsProcessImpl(Host host, String serverDirectory, Optional<Duration> optionalTimeout,
|
||||
byte[] privateKeyEncryptionPassphrase)
|
||||
throws NumberFormatException, JSchException, IOException, InterruptedException {
|
||||
super(host, serverDirectory, optionalTimeout);
|
||||
super(host, serverDirectory, optionalTimeout, privateKeyEncryptionPassphrase);
|
||||
}
|
||||
|
||||
public SailingAnalyticsProcessImpl(int port, Host host, String serverDirectory) {
|
||||
@@ -60,7 +61,7 @@ implements SailingAnalyticsProcess<ShardingKey> {
|
||||
* information about server name as well as availability and replication status can be obtained.
|
||||
*/
|
||||
@Override
|
||||
public String getServerName(Optional<Duration> optionalTimeout)
|
||||
public String getServerName(Optional<Duration> optionalTimeout, byte[] privateKeyEncryptionPassphrase)
|
||||
throws JSchException, IOException, InterruptedException, SftpException, ParseException {
|
||||
String result = null;
|
||||
final TimePoint start = TimePoint.now();
|
||||
|
||||
+19
-5
@@ -45,6 +45,7 @@ implements Procedure<ShardingKey, SailingAnalyticsMetrics, SailingAnalyticsProce
|
||||
private final ApplicationProcessHost<ShardingKey, SailingAnalyticsMetrics, SailingAnalyticsProcess<ShardingKey>> hostToDeployTo;
|
||||
private final ApplicationConfigurationT applicationConfiguration;
|
||||
private final Optional<Duration> optionalTimeout;
|
||||
private byte[] privateKeyEncryptionPassphrase;
|
||||
|
||||
/**
|
||||
* The process launched by this procedure. {@link #hostToDeployTo} is expected to be identical to
|
||||
@@ -88,6 +89,7 @@ implements Procedure<ShardingKey, SailingAnalyticsMetrics, SailingAnalyticsProce
|
||||
ApplicationConfigurationBuilderT extends SailingAnalyticsApplicationConfiguration.Builder<ApplicationConfigurationBuilderT, ApplicationConfigurationT, ShardingKey>>
|
||||
extends com.sap.sse.landscape.orchestration.Procedure.Builder<BuilderT, DeployProcessOnMultiServer<ShardingKey, HostT, ApplicationConfigurationT, ApplicationConfigurationBuilderT>, ShardingKey, SailingAnalyticsMetrics, SailingAnalyticsProcess<ShardingKey>> {
|
||||
BuilderT setHostToDeployTo(ApplicationProcessHost<ShardingKey, SailingAnalyticsMetrics, SailingAnalyticsProcess<ShardingKey>> hostToDeployTo);
|
||||
BuilderT setPrivateKeyEncryptionPassphrase(byte[] privateKeyEncryptionPassphrase);
|
||||
}
|
||||
|
||||
public static class BuilderImpl<BuilderT extends Builder<BuilderT, ShardingKey, HostT, ApplicationConfigurationT, ApplicationConfigurationBuilderT>, ShardingKey, HostT extends AwsInstance<ShardingKey, SailingAnalyticsMetrics>,
|
||||
@@ -97,6 +99,7 @@ implements Procedure<ShardingKey, SailingAnalyticsMetrics, SailingAnalyticsProce
|
||||
implements Builder<BuilderT, ShardingKey, HostT, ApplicationConfigurationT, ApplicationConfigurationBuilderT> {
|
||||
private final SailingAnalyticsApplicationConfiguration.BuilderImpl<ApplicationConfigurationBuilderT, ApplicationConfigurationT, ShardingKey> applicationConfigurationBuilder;
|
||||
private ApplicationProcessHost<ShardingKey, SailingAnalyticsMetrics, SailingAnalyticsProcess<ShardingKey>> hostToDeployTo;
|
||||
private byte[] privateKeyEncryptionPassphrase;
|
||||
|
||||
protected BuilderImpl(SailingAnalyticsApplicationConfiguration.BuilderImpl<ApplicationConfigurationBuilderT, ApplicationConfigurationT, ShardingKey> applicationConfigurationBuilder) {
|
||||
super();
|
||||
@@ -110,7 +113,7 @@ implements Procedure<ShardingKey, SailingAnalyticsMetrics, SailingAnalyticsProce
|
||||
if (!getApplicationConfigurationBuilder().isServerDirectorySet()) {
|
||||
getApplicationConfigurationBuilder().setServerDirectory(ApplicationProcessHost.DEFAULT_SERVERS_PATH+"/"+getApplicationConfigurationBuilder().getServerName());
|
||||
}
|
||||
final Iterable<SailingAnalyticsProcess<ShardingKey>> applicationProcesses = getHostToDeployTo().getApplicationProcesses(getOptionalTimeout());
|
||||
final Iterable<SailingAnalyticsProcess<ShardingKey>> applicationProcesses = getHostToDeployTo().getApplicationProcesses(getOptionalTimeout(), privateKeyEncryptionPassphrase);
|
||||
if (!getApplicationConfigurationBuilder().isPortSet()) {
|
||||
getApplicationConfigurationBuilder().setPort(getNextAvailablePort(applicationProcesses,
|
||||
SailingAnalyticsApplicationConfiguration.Builder.DEFAULT_PORT,
|
||||
@@ -121,7 +124,7 @@ implements Procedure<ShardingKey, SailingAnalyticsMetrics, SailingAnalyticsProce
|
||||
SailingAnalyticsApplicationConfiguration.Builder.DEFAULT_TELNET_PORT,
|
||||
ap->{
|
||||
try {
|
||||
return ap.getTelnetPortToOSGiConsole(getOptionalTimeout());
|
||||
return ap.getTelnetPortToOSGiConsole(getOptionalTimeout(), privateKeyEncryptionPassphrase);
|
||||
} catch (NumberFormatException | JSchException | IOException | SftpException
|
||||
| InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
@@ -133,7 +136,7 @@ implements Procedure<ShardingKey, SailingAnalyticsMetrics, SailingAnalyticsProce
|
||||
SailingAnalyticsApplicationConfiguration.Builder.DEFAULT_EXPEDITION_PORT,
|
||||
ap->{
|
||||
try {
|
||||
return ap.getExpeditionUdpPort(getOptionalTimeout());
|
||||
return ap.getExpeditionUdpPort(getOptionalTimeout(), privateKeyEncryptionPassphrase);
|
||||
} catch (NumberFormatException | JSchException | IOException | InterruptedException
|
||||
| SftpException e) {
|
||||
throw new RuntimeException(e);
|
||||
@@ -197,6 +200,16 @@ implements Procedure<ShardingKey, SailingAnalyticsMetrics, SailingAnalyticsProce
|
||||
protected Optional<Duration> getOptionalTimeout() {
|
||||
return super.getOptionalTimeout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderT setPrivateKeyEncryptionPassphrase(byte[] privateKeyEncryptionPassphrase) {
|
||||
this.privateKeyEncryptionPassphrase = privateKeyEncryptionPassphrase;
|
||||
return self();
|
||||
}
|
||||
|
||||
protected byte[] getPrivateKeyEncryptionPassphrase() {
|
||||
return privateKeyEncryptionPassphrase;
|
||||
}
|
||||
}
|
||||
|
||||
public static <BuilderT extends Builder<BuilderT, ShardingKey, HostT, ApplicationConfigurationT, ApplicationConfigurationBuilderT>, ShardingKey, HostT extends AwsInstance<ShardingKey, SailingAnalyticsMetrics>,
|
||||
@@ -213,6 +226,7 @@ implements Procedure<ShardingKey, SailingAnalyticsMetrics, SailingAnalyticsProce
|
||||
super(builder);
|
||||
this.hostToDeployTo = builder.getHostToDeployTo();
|
||||
this.optionalTimeout = builder.getOptionalTimeout();
|
||||
this.privateKeyEncryptionPassphrase = builder.getPrivateKeyEncryptionPassphrase();
|
||||
this.applicationConfiguration = builder.getApplicationConfigurationBuilder().build();
|
||||
assert getHostToDeployTo() != null;
|
||||
}
|
||||
@@ -222,7 +236,7 @@ implements Procedure<ShardingKey, SailingAnalyticsMetrics, SailingAnalyticsProce
|
||||
assert getHostToDeployTo() != null;
|
||||
final String serverDirectory = applicationConfiguration.getServerDirectory();
|
||||
{
|
||||
final SshCommandChannel sshChannel = getHostToDeployTo().createSshChannel("sailing", optionalTimeout);
|
||||
final SshCommandChannel sshChannel = getHostToDeployTo().createSshChannel("sailing", optionalTimeout, privateKeyEncryptionPassphrase);
|
||||
logger.info("stdout: "+sshChannel.runCommandAndReturnStdoutAndLogStderr(
|
||||
"mkdir -p "+serverDirectory+"; "+
|
||||
"cd "+serverDirectory+"; "+
|
||||
@@ -231,7 +245,7 @@ implements Procedure<ShardingKey, SailingAnalyticsMetrics, SailingAnalyticsProce
|
||||
"stderr: ", Level.WARNING));
|
||||
}
|
||||
{
|
||||
final SshCommandChannel sshChannel = getHostToDeployTo().createRootSshChannel(optionalTimeout);
|
||||
final SshCommandChannel sshChannel = getHostToDeployTo().createRootSshChannel(optionalTimeout, privateKeyEncryptionPassphrase);
|
||||
logger.info("stdout: "+sshChannel.runCommandAndReturnStdoutAndLogStderr("service httpd reload", "stderr: ", Level.WARNING));
|
||||
}
|
||||
process = new SailingAnalyticsProcessImpl<>(applicationConfiguration.getPort(), getHostToDeployTo(), serverDirectory);
|
||||
|
||||
+4
-4
@@ -96,7 +96,7 @@ implements StartFromSailingAnalyticsImage {
|
||||
result = (instanceId, az, landscape)->
|
||||
new ApplicationProcessHostImpl<ShardingKey, SailingAnalyticsMetrics, SailingAnalyticsProcess<ShardingKey>>(instanceId, az, landscape, (host, serverDirectory)->{
|
||||
try {
|
||||
return new SailingAnalyticsProcessImpl<ShardingKey>(host, serverDirectory, getOptionalTimeout());
|
||||
return new SailingAnalyticsProcessImpl<ShardingKey>(host, serverDirectory, getOptionalTimeout(), getPrivateKeyEncryptionPassphrase());
|
||||
} catch (NumberFormatException | JSchException | IOException | InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@@ -141,11 +141,11 @@ implements StartFromSailingAnalyticsImage {
|
||||
super.run(); // this will trigger the "sailing" init.d script running in the background, triggering the image upgrade, then the httpd stop and clean-up
|
||||
copyRootAuthorizedKeysToOtherUser(SAILING_USER_NAME, optionalTimeout);
|
||||
final String instanceId = getHost().getInstanceId();
|
||||
getHost().getReverseProxy().createInternalStatusRedirect(optionalTimeout);
|
||||
getHost().getReverseProxy().createInternalStatusRedirect(optionalTimeout, getPrivateKeyEncryptionPassphrase());
|
||||
boolean fileFound = false;
|
||||
final TimePoint startedToWaitForImageUpgradeToFinish = TimePoint.now();
|
||||
do {
|
||||
final ChannelSftp sftpChannel = getHost().createRootSftpChannel(optionalTimeout);
|
||||
final ChannelSftp sftpChannel = getHost().createRootSftpChannel(optionalTimeout, getPrivateKeyEncryptionPassphrase());
|
||||
sftpChannel.connect();
|
||||
try {
|
||||
final SftpATTRS stat = sftpChannel.stat("/tmp/image-upgrade-finished");
|
||||
@@ -160,7 +160,7 @@ implements StartFromSailingAnalyticsImage {
|
||||
// wait until the file indicating the finishing of the image upgrade process was found
|
||||
// or, if a timeout was provided, the timeout expired
|
||||
} while (!fileFound && optionalTimeout.map(timeout->startedToWaitForImageUpgradeToFinish.plus(timeout).after(TimePoint.now())).orElse(true));
|
||||
final SshCommandChannel sshCommandChannel = getHost().createRootSshChannel(optionalTimeout);
|
||||
final SshCommandChannel sshCommandChannel = getHost().createRootSshChannel(optionalTimeout, getPrivateKeyEncryptionPassphrase());
|
||||
logger.info("stdout for removing "+ApplicationProcessHost.DEFAULT_SERVER_PATH+" and starting httpd service on instance "+instanceId+": "+
|
||||
sshCommandChannel.runCommandAndReturnStdoutAndLogStderr("rm -rf "+ApplicationProcessHost.DEFAULT_SERVER_PATH+"; service httpd start",
|
||||
"stderr for removing "+ApplicationProcessHost.DEFAULT_SERVER_PATH+" and starting httpd service on instance \"+instanceId+\": ", Level.INFO));
|
||||
|
||||
+1
-1
@@ -93,7 +93,7 @@ implements Procedure<ShardingKey, SailingAnalyticsMetrics, SailingAnalyticsProce
|
||||
new ApplicationProcessHostImpl<>(instanceId, az, landscape,
|
||||
(host, serverDirectory)->{
|
||||
try {
|
||||
return new SailingAnalyticsProcessImpl<ShardingKey>(host, serverDirectory, getOptionalTimeout());
|
||||
return new SailingAnalyticsProcessImpl<ShardingKey>(host, serverDirectory, getOptionalTimeout(), getPrivateKeyEncryptionPassphrase());
|
||||
} catch (NumberFormatException | JSchException | IOException | InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
+1
-1
@@ -208,7 +208,7 @@ implements Procedure<ShardingKey, SailingAnalyticsMetrics, SailingAnalyticsProce
|
||||
new ApplicationProcessHostImpl<>(instanceId, az, landscape,
|
||||
(host, serverDirectory)->{
|
||||
try {
|
||||
return new SailingAnalyticsProcessImpl<ShardingKey>(host, serverDirectory, getOptionalTimeout());
|
||||
return new SailingAnalyticsProcessImpl<ShardingKey>(host, serverDirectory, getOptionalTimeout(), getPrivateKeyEncryptionPassphrase());
|
||||
} catch (NumberFormatException | JSchException | IOException | InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package com.sap.sse.gwt.client.celltable;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.google.gwt.core.client.GWT;
|
||||
import com.google.gwt.user.cellview.client.Column;
|
||||
import com.google.gwt.user.cellview.client.ColumnSortEvent.ListHandler;
|
||||
import com.google.gwt.user.cellview.client.SimplePager;
|
||||
import com.google.gwt.user.cellview.client.TextColumn;
|
||||
import com.google.gwt.user.client.ui.IsWidget;
|
||||
import com.google.gwt.user.client.ui.VerticalPanel;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
@@ -14,6 +16,7 @@ import com.google.gwt.view.client.ListDataProvider;
|
||||
import com.google.gwt.view.client.MultiSelectionModel;
|
||||
import com.google.gwt.view.client.Range;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.util.NaturalComparator;
|
||||
import com.sap.sse.gwt.client.ErrorReporter;
|
||||
import com.sap.sse.gwt.client.StringMessages;
|
||||
import com.sap.sse.gwt.client.panels.AbstractFilterablePanel;
|
||||
@@ -125,6 +128,20 @@ public abstract class TableWrapper<T, S extends RefreshableSelectionModel<T>, SM
|
||||
boatColumnListHandler.setComparator(column, comparator);
|
||||
addColumn(column, header);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a sortable {@link TextColumn} whose {@link TextColumn#getValue(Object)} method is based on the {@code valueMapper}
|
||||
* and whose sorting is based on a {@link NaturalComparator}.
|
||||
*/
|
||||
public void addColumn(Function<T, String> valueMapper, String header) {
|
||||
final TextColumn<T> textColumn = new TextColumn<T>() {
|
||||
@Override
|
||||
public String getValue(T object) {
|
||||
return valueMapper.apply(object);
|
||||
}
|
||||
};
|
||||
addColumn(textColumn, header, Comparator.comparing(t->textColumn.getValue(t), new NaturalComparator()));
|
||||
}
|
||||
|
||||
public void setEmptyTableWidget(Widget widget) {
|
||||
table.setEmptyTableWidget(widget);
|
||||
|
||||
+88
-25
@@ -15,6 +15,7 @@ import java.net.InetAddress;
|
||||
import java.net.URL;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Base64;
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
@@ -23,7 +24,6 @@ import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.jcraft.jsch.ChannelSftp;
|
||||
@@ -32,6 +32,7 @@ import com.jcraft.jsch.JSchException;
|
||||
import com.jcraft.jsch.KeyPair;
|
||||
import com.jcraft.jsch.SftpException;
|
||||
import com.sap.sailing.landscape.SailingAnalyticsMetrics;
|
||||
import com.sap.sailing.landscape.SailingReleaseRepository;
|
||||
import com.sap.sailing.landscape.impl.SailingAnalyticsProcessImpl;
|
||||
import com.sap.sailing.landscape.procedures.StartFromSailingAnalyticsImage;
|
||||
import com.sap.sse.common.Duration;
|
||||
@@ -43,6 +44,8 @@ import com.sap.sse.landscape.application.ApplicationProcess;
|
||||
import com.sap.sse.landscape.aws.impl.AwsRegion;
|
||||
import com.sap.sse.landscape.aws.orchestration.CreateDNSBasedLoadBalancerMapping;
|
||||
import com.sap.sse.landscape.impl.ReleaseRepositoryImpl;
|
||||
import com.sap.sse.landscape.mongodb.MongoEndpoint;
|
||||
import com.sap.sse.landscape.mongodb.impl.DatabaseImpl;
|
||||
import com.sap.sse.landscape.ssh.SSHKeyPair;
|
||||
import com.sap.sse.landscape.ssh.SshCommandChannel;
|
||||
|
||||
@@ -72,26 +75,72 @@ public class ConnectivityTest<ProcessT extends ApplicationProcess<String, Sailin
|
||||
private AwsLandscape<String, SailingAnalyticsMetrics, ProcessT> landscape;
|
||||
private AwsRegion region;
|
||||
private byte[] keyPass;
|
||||
private String AXELS_KEY_PASS;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
landscape = AwsLandscape.obtain();
|
||||
region = new AwsRegion(Region.EU_WEST_2);
|
||||
AXELS_KEY_PASS = new String(Base64.getDecoder().decode(System.getProperty("axelskeypassphrase")));
|
||||
keyPass = "lkayrelakuesyrlasp8caorewyc".getBytes();
|
||||
}
|
||||
|
||||
@Ignore("Fill in key details for the key used to launch the central reverse proxy in the test landscape")
|
||||
//@Ignore("Fill in key details for the key used to launch the central reverse proxy in the test landscape")
|
||||
@Test
|
||||
public void readAndStoreSSHKey() throws JSchException, IOException, InterruptedException {
|
||||
final String AXELS_KEY_NAME = "The key name goes here";
|
||||
final String KEY_PASSPHRASE = "your passphrase goes here";
|
||||
final String PATH_TO_YOUR_PRIVATE_KEY = "the path to your id_rsa file";
|
||||
final String AXELS_KEY_NAME = "Axel";
|
||||
final String PATH_TO_YOUR_PRIVATE_KEY = "c:/Users/d043530/.ssh/id_rsa";
|
||||
landscape.deleteKeyPair(region, AXELS_KEY_NAME);
|
||||
final KeyPair keyPair = KeyPair.load(new JSch(), PATH_TO_YOUR_PRIVATE_KEY, PATH_TO_YOUR_PRIVATE_KEY+".pub");
|
||||
final byte[] pubKeyBytes = getPublicKeyBytes(keyPair);
|
||||
final byte[] privKeyBytes = getPrivateKeyBytes(keyPair, KEY_PASSPHRASE.getBytes());
|
||||
landscape.importKeyPair(region, pubKeyBytes, privKeyBytes, AXELS_KEY_NAME);
|
||||
final SshCommandChannel sshChannel = landscape.getCentralReverseProxy(region).getHosts().iterator().next().createRootSshChannel(optionalTimeout);
|
||||
// WORKS:
|
||||
// 1) decrypt private key read from file containing encrypted key
|
||||
// 2) obtain bytes of private key unencrypted
|
||||
// 3) "load"/create new KeyPair from private key decrypted this way
|
||||
// 4) encrypt private key based on KeyPair loaded from decrypted private key
|
||||
// 5) "load"/create new KeyPair from key *encrypted* this way
|
||||
// 6) decryption of private key works
|
||||
final ByteArrayOutputStream privateKey = new ByteArrayOutputStream();
|
||||
keyPair.decrypt(AXELS_KEY_PASS.getBytes());
|
||||
keyPair.writePrivateKey(privateKey); // writing plain unencrypted private key WORKS
|
||||
final KeyPair keyPairReadFromFileAndDescrypted = KeyPair.load(new JSch(), privateKey.toByteArray(), pubKeyBytes);
|
||||
assertFalse(keyPairReadFromFileAndDescrypted.isEncrypted());
|
||||
final ByteArrayOutputStream privateKeyEncrypted = new ByteArrayOutputStream();
|
||||
keyPairReadFromFileAndDescrypted.writePrivateKey(privateKeyEncrypted, AXELS_KEY_PASS.getBytes());
|
||||
final KeyPair toAndFrom = KeyPair.load(new JSch(), privateKeyEncrypted.toByteArray(), pubKeyBytes);
|
||||
assertTrue(toAndFrom.isEncrypted());
|
||||
assertTrue(toAndFrom.decrypt(AXELS_KEY_PASS.getBytes()));
|
||||
assertFalse(toAndFrom.isEncrypted());
|
||||
// WORKS:
|
||||
// 1) decrypt private key read from file containing encrypted key using getDecryptedPrivateKeyBytes(...)
|
||||
// 2) obtain bytes of private key unencrypted
|
||||
// 3) "load"/create new KeyPair from private key decrypted this way
|
||||
// 4) encrypt private key based on KeyPair loaded from encrypted private key
|
||||
// 5) "load"/create new KeyPair from key *encrypted* this way
|
||||
// 6) decryption of private key works
|
||||
final byte[] privKeyBytesDecrypted = getDecryptedPrivateKeyBytes(keyPair, AXELS_KEY_PASS.getBytes());
|
||||
final KeyPair testKeyFromDecryptedBytes = KeyPair.load(new JSch(), privKeyBytesDecrypted, pubKeyBytes);
|
||||
assertFalse(testKeyFromDecryptedBytes.isEncrypted());
|
||||
final ByteArrayOutputStream encryptedPrivateKeyFromDecrypted = new ByteArrayOutputStream();
|
||||
testKeyFromDecryptedBytes.writePrivateKey(encryptedPrivateKeyFromDecrypted, AXELS_KEY_PASS.getBytes());
|
||||
final KeyPair testKeyFromEncryptedBytes = KeyPair.load(new JSch(), encryptedPrivateKeyFromDecrypted.toByteArray(), pubKeyBytes);
|
||||
assertTrue(testKeyFromEncryptedBytes.isEncrypted());
|
||||
assertTrue(testKeyFromEncryptedBytes.decrypt(AXELS_KEY_PASS.getBytes()));
|
||||
assertFalse(testKeyFromEncryptedBytes.isEncrypted());
|
||||
// BROKEN:
|
||||
// 1) decrypt private key read from file containing encrypted key
|
||||
// 2) obtain bytes of private key *encrypted* (writePrivateKey with non-null passphrase)
|
||||
// 3) "load"/create new KeyPair from *encrypted* private key
|
||||
// 4) decryption of private key FAILS
|
||||
// We manifest this BROKENNESS here in this test case:
|
||||
final byte[] privKeyBytesEncrypted = getPrivateKeyBytes(keyPair, AXELS_KEY_PASS.getBytes());
|
||||
final KeyPair testKeyFromBytes = KeyPair.load(new JSch(), privKeyBytesEncrypted, pubKeyBytes);
|
||||
assertTrue(testKeyFromBytes.isEncrypted());
|
||||
assertFalse(testKeyFromBytes.decrypt(AXELS_KEY_PASS.getBytes())); // THIS IS THE STRANGE CASE; I THINK IT SHOULD HAVE WORKED!
|
||||
assertTrue(testKeyFromBytes.isEncrypted()); // AND AS A CONSEQUENCE THE KEY IS STILL ENCRYPTED!
|
||||
// In order to use this to import a valid key existing pair we'll import the last valid key, namely testKeyFromEncryptedBytes:
|
||||
landscape.importKeyPair(region, pubKeyBytes, encryptedPrivateKeyFromDecrypted.toByteArray(), AXELS_KEY_NAME);
|
||||
final SshCommandChannel sshChannel = landscape.getCentralReverseProxy(region).getHosts().iterator().next().createRootSshChannel(optionalTimeout, AXELS_KEY_PASS.getBytes());
|
||||
final String stdout = sshChannel.runCommandAndReturnStdoutAndLogStderr("ls -al", /* stderr prefix */ null, /* stderr log level */ null);
|
||||
assertFalse(stdout.isEmpty());
|
||||
}
|
||||
@@ -105,16 +154,16 @@ public class ConnectivityTest<ProcessT extends ApplicationProcess<String, Sailin
|
||||
final String hostname = "kw2021.sapsailing.com";
|
||||
final AwsInstance<String, SailingAnalyticsMetrics> proxyHost = proxy.getHosts().iterator().next();
|
||||
final ProcessT process = createApplicationProcess(proxyHost);
|
||||
proxy.setEventRedirect(hostname, process, UUID.randomUUID());
|
||||
proxy.setEventRedirect(hostname, process, UUID.randomUUID(), AXELS_KEY_PASS.getBytes());
|
||||
final ByteArrayOutputStream configFileContents = new ByteArrayOutputStream();
|
||||
final ChannelSftp sftpChannel = proxyHost.createRootSftpChannel(optionalTimeout);
|
||||
final ChannelSftp sftpChannel = proxyHost.createRootSftpChannel(optionalTimeout, AXELS_KEY_PASS.getBytes());
|
||||
sftpChannel.connect((int) optionalTimeout.orElse(Duration.NULL).asMillis());
|
||||
final String configFileName = "/etc/httpd/conf.d/"+hostname+".conf";
|
||||
sftpChannel.get(configFileName, configFileContents);
|
||||
assertTrue(configFileContents.toString().startsWith("Use Event-SSL "+hostname));
|
||||
sftpChannel.disconnect();
|
||||
proxy.removeRedirect(hostname);
|
||||
final SshCommandChannel lsSshChannel = proxyHost.createRootSshChannel(optionalTimeout);
|
||||
proxy.removeRedirect(hostname, AXELS_KEY_PASS.getBytes());
|
||||
final SshCommandChannel lsSshChannel = proxyHost.createRootSshChannel(optionalTimeout, AXELS_KEY_PASS.getBytes());
|
||||
final String lsOutput = lsSshChannel.runCommandAndReturnStdoutAndLogStderr("ls "+configFileName, /* stderr prefix */ null, /* stderr log level */ null);
|
||||
assertTrue(lsOutput.isEmpty());
|
||||
}
|
||||
@@ -133,10 +182,13 @@ public class ConnectivityTest<ProcessT extends ApplicationProcess<String, Sailin
|
||||
final String hostname = "test-"+new Random().nextInt()+"."+hostedZoneName;
|
||||
final String keyName = "MyKey-"+UUID.randomUUID();
|
||||
createKeyPair(keyName);
|
||||
final MongoEndpoint mongoEndpoint = landscape.getMongoEndpoints(region).iterator().next();
|
||||
final AwsInstance<String, SailingAnalyticsMetrics> host = landscape.launchHost(
|
||||
getLatestSailingImage(),
|
||||
InstanceType.T3_SMALL, landscape.getAvailabilityZoneByName(region, "eu-west-2b"), keyName, Collections.singleton(()->"sg-0b2afd48960251280"),
|
||||
Optional.of(Tags.with("Name", "MyHost").and("Hello", "World")));
|
||||
Optional.of(Tags.with("Name", "MyHost").and("Hello", "World")),
|
||||
"MONGODB_URI=\""+mongoEndpoint.getURI(Optional.of(new DatabaseImpl(mongoEndpoint, "winddbTest")))+"\"",
|
||||
"INSTALL_FROM_RELEASE="+SailingReleaseRepository.INSTANCE.getLatestRelease("bug4811").getName());
|
||||
try {
|
||||
assertNotNull(host);
|
||||
final Instance instance = landscape.getInstance(host.getInstanceId(), region);
|
||||
@@ -155,12 +207,12 @@ public class ConnectivityTest<ProcessT extends ApplicationProcess<String, Sailin
|
||||
final ProcessT process = createApplicationProcess(host);
|
||||
process.waitUntilReady(optionalTimeout);
|
||||
// check env.sh access
|
||||
final String envSh = process.getEnvSh(optionalTimeout);
|
||||
final String envSh = process.getEnvSh(optionalTimeout, keyPass);
|
||||
assertFalse(envSh.isEmpty());
|
||||
assertTrue(envSh.contains("SERVER_NAME="));
|
||||
final Release release = process.getRelease(new ReleaseRepositoryImpl("http://releases.sapsailing.com", "build"), optionalTimeout);
|
||||
final Release release = process.getRelease(new ReleaseRepositoryImpl("http://releases.sapsailing.com", "build"), optionalTimeout, keyPass);
|
||||
assertNotNull(release);
|
||||
assertEquals(14888, process.getTelnetPortToOSGiConsole(optionalTimeout));
|
||||
assertEquals(14888, process.getTelnetPortToOSGiConsole(optionalTimeout, keyPass));
|
||||
final AwsLandscape<String, SailingAnalyticsMetrics, ProcessT> castLandscape = (AwsLandscape<String, SailingAnalyticsMetrics, ProcessT>) landscape;
|
||||
final CreateDNSBasedLoadBalancerMapping.Builder<?, ?, String, SailingAnalyticsMetrics, ProcessT, AwsInstance<String, SailingAnalyticsMetrics>> builder = CreateDNSBasedLoadBalancerMapping.builder();
|
||||
builder
|
||||
@@ -176,7 +228,7 @@ public class ConnectivityTest<ProcessT extends ApplicationProcess<String, Sailin
|
||||
createDNSBasedLoadBalancerMappingProcedure.run();
|
||||
assertNotNull(createDNSBasedLoadBalancerMappingProcedure.getLoadBalancerUsed());
|
||||
assertNotNull(createDNSBasedLoadBalancerMappingProcedure.getMasterTargetGroupCreated());
|
||||
assertEquals(TARGET_GROUP_NAME_PREFIX+process.getServerName(optionalTimeout), createDNSBasedLoadBalancerMappingProcedure.getPublicTargetGroupCreated().getName());
|
||||
assertEquals(TARGET_GROUP_NAME_PREFIX+process.getServerName(optionalTimeout, keyPass), createDNSBasedLoadBalancerMappingProcedure.getPublicTargetGroupCreated().getName());
|
||||
} finally {
|
||||
if (createDNSBasedLoadBalancerMappingProcedure.getLoadBalancerUsed() != null) {
|
||||
createDNSBasedLoadBalancerMappingProcedure.getLoadBalancerUsed().delete();
|
||||
@@ -218,6 +270,9 @@ public class ConnectivityTest<ProcessT extends ApplicationProcess<String, Sailin
|
||||
keyPair.writePublicKey(publicKey, publicKeyComment);
|
||||
final KeyPair keyPairReadFromFile = KeyPair.load(jsch, privateKey.toByteArray(), publicKey.toByteArray());
|
||||
assertEquals(publicKeyComment, keyPairReadFromFile.getPublicKeyComment());
|
||||
assertTrue(keyPairReadFromFile.isEncrypted());
|
||||
assertTrue(keyPairReadFromFile.decrypt(keyPass));
|
||||
assertFalse(keyPairReadFromFile.isEncrypted());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -226,7 +281,7 @@ public class ConnectivityTest<ProcessT extends ApplicationProcess<String, Sailin
|
||||
final JSch jsch = new JSch();
|
||||
final KeyPair keyPair = KeyPair.genKeyPair(jsch, KeyPair.RSA, 4096);
|
||||
final byte[] pubKeyBytes = getPublicKeyBytes(keyPair);
|
||||
final byte[] privKeyBytes = getPrivateKeyBytes(keyPair, /* passphrase */ null);
|
||||
final byte[] privKeyBytes = getDecryptedPrivateKeyBytes(keyPair, /* passphrase */ null);
|
||||
final String keyId = landscape.importKeyPair(region, pubKeyBytes, privKeyBytes, testKeyName);
|
||||
assertTrue(keyId.startsWith("key-"));
|
||||
final KeyPairInfo awsKeyPairInfo = landscape.getKeyPairInfo(region, testKeyName);
|
||||
@@ -241,16 +296,24 @@ public class ConnectivityTest<ProcessT extends ApplicationProcess<String, Sailin
|
||||
return pubKeyBytes;
|
||||
}
|
||||
|
||||
private byte[] getPrivateKeyBytes(final KeyPair keyPair, final byte[] passphrase) {
|
||||
if (!keyPair.decrypt(passphrase)) { // need to decrypt before writePrivateKey would work
|
||||
private byte[] getDecryptedPrivateKeyBytes(final KeyPair keyPair, final byte[] passphrase) {
|
||||
return getPrivateKeyBytes(keyPair, passphrase, null);
|
||||
}
|
||||
|
||||
private byte[] getPrivateKeyBytes(final KeyPair keyPair, final byte[] passphraseForDecryption, final byte[] passphraseForEncryption) {
|
||||
if (!keyPair.decrypt(passphraseForDecryption)) { // need to decrypt before writePrivateKey would work
|
||||
throw new IllegalArgumentException("Passphrase didn't unlock private key of key pair "+keyPair);
|
||||
}
|
||||
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
keyPair.writePrivateKey(bos, /* passphrase */ null);
|
||||
keyPair.writePrivateKey(bos, passphraseForEncryption);
|
||||
final byte[] privKeyBytes = bos.toByteArray();
|
||||
return privKeyBytes;
|
||||
}
|
||||
|
||||
private byte[] getPrivateKeyBytes(final KeyPair keyPair, byte[] passphrase) {
|
||||
return getPrivateKeyBytes(keyPair, passphrase, passphrase);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSshConnectWithCreatedKey() throws JSchException, InterruptedException, IOException {
|
||||
final String keyName = "MyKey-"+UUID.randomUUID();
|
||||
@@ -262,7 +325,7 @@ public class ConnectivityTest<ProcessT extends ApplicationProcess<String, Sailin
|
||||
public void testSshConnectWithImportedKey() throws JSchException, InterruptedException, IOException {
|
||||
final String keyName = "MyKey-"+UUID.randomUUID();
|
||||
final KeyPair keyPair = KeyPair.genKeyPair(new JSch(), KeyPair.RSA, 4096);
|
||||
landscape.importKeyPair(region, getPublicKeyBytes(keyPair), getPrivateKeyBytes(keyPair, /* passphrase */ null), keyName);
|
||||
landscape.importKeyPair(region, getPublicKeyBytes(keyPair), getDecryptedPrivateKeyBytes(keyPair, /* passphrase */ null), keyName);
|
||||
testSshConnectWithKey(keyName);
|
||||
}
|
||||
|
||||
@@ -277,13 +340,13 @@ public class ConnectivityTest<ProcessT extends ApplicationProcess<String, Sailin
|
||||
InetAddress address = host.getPublicAddress(optionalTimeout);
|
||||
assertNotNull(address);
|
||||
logger.info("Obtained public IP address "+address);
|
||||
SshCommandChannel shellChannel = host.createRootSshChannel(optionalTimeout);
|
||||
SshCommandChannel shellChannel = host.createRootSshChannel(optionalTimeout, keyPass);
|
||||
assertNotNull(shellChannel);
|
||||
logger.info("Shell channel connected. Waiting for it to become responsive...");
|
||||
final String stdout = shellChannel.runCommandAndReturnStdoutAndLogStderr("pwd", /* stderr prefix */ null, /* stderr log level */ Level.WARNING);
|
||||
assertEquals("/root\n", turnAllLineSeparatorsIntoLineFeed(stdout));
|
||||
// now try a simple command, checking for the "init" process to be found
|
||||
final SshCommandChannel commandChannel = host.createRootSshChannel(Optional.empty());
|
||||
final SshCommandChannel commandChannel = host.createRootSshChannel(Optional.empty(), keyPass);
|
||||
final String processToLookFor = "init";
|
||||
final String output = commandChannel.runCommandAndReturnStdoutAndLogStderr("ps axlw | grep "+processToLookFor, /* stderr prefix */ null, /* stderr log level */ null);
|
||||
assertTrue(output.contains(processToLookFor));
|
||||
@@ -299,7 +362,7 @@ public class ConnectivityTest<ProcessT extends ApplicationProcess<String, Sailin
|
||||
}
|
||||
|
||||
private void createKeyPair(final String keyName) throws JSchException {
|
||||
final SSHKeyPair sshKeyPair = landscape.createKeyPair(region, keyName);
|
||||
final SSHKeyPair sshKeyPair = landscape.createKeyPair(region, keyName, keyPass);
|
||||
assertNotNull(sshKeyPair);
|
||||
assertEquals(keyName, sshKeyPair.getName());
|
||||
}
|
||||
|
||||
+2
-1
@@ -32,8 +32,9 @@ extends AwsInstance<ShardingKey, MetricsT> {
|
||||
/**
|
||||
* Obtains the Sailing Analytics processes running on this host. Can be zero or more.
|
||||
* @param optionalTimeout TODO
|
||||
* @param privateKeyEncryptionPassphrase TODO
|
||||
*/
|
||||
Iterable<ProcessT> getApplicationProcesses(Optional<Duration> optionalTimeout) throws SftpException, JSchException, IOException, InterruptedException;
|
||||
Iterable<ProcessT> getApplicationProcesses(Optional<Duration> optionalTimeout, byte[] privateKeyEncryptionPassphrase) throws SftpException, JSchException, IOException, InterruptedException;
|
||||
|
||||
AwsLandscape<ShardingKey, MetricsT, ProcessT> getLandscape();
|
||||
}
|
||||
@@ -221,24 +221,33 @@ extends Landscape<ShardingKey, MetricsT, ProcessT> {
|
||||
/**
|
||||
* Uploads the public key to AWS under the name "keyName", stores it in this landscape and returns the key pair ID
|
||||
*/
|
||||
String importKeyPair(Region region, byte[] publicKey, byte[] unencryptedPrivateKey, String keyName) throws JSchException;
|
||||
String importKeyPair(Region region, byte[] publicKey, byte[] encryptedPrivateKey, String keyName) throws JSchException;
|
||||
|
||||
void terminate(AwsInstance<ShardingKey, MetricsT> host);
|
||||
|
||||
SSHKeyPair getSSHKeyPair(Region region, String keyName);
|
||||
|
||||
byte[] getDecryptedPrivateKey(SSHKeyPair keyPair) throws JSchException;
|
||||
/**
|
||||
* Assumes that the {@code keyPair}'s private key has been encrypted using this landscape's default encryption passphrase
|
||||
* and uses that to decrypt it.
|
||||
*/
|
||||
byte[] getDecryptedPrivateKey(SSHKeyPair keyPair, byte[] privateKeyEncryptionPassphrase) throws JSchException;
|
||||
|
||||
void addSSHKeyPair(com.sap.sse.landscape.Region region, String creator, String keyName, KeyPair keyPairWithDecryptedPrivateKey);
|
||||
/**
|
||||
* Adds a key pair with {@link KeyPair#decrypt(byte[]) decrypted} private key to the AWS {@code region} identified
|
||||
* and stores it persistently also in the local server's database with the private key encrypted.
|
||||
*/
|
||||
void addSSHKeyPair(com.sap.sse.landscape.Region region, String creator, String keyName, KeyPair keyPairWithDecryptedPrivateKey) throws JSchException;
|
||||
|
||||
/**
|
||||
* Creates a key pair with the given name in the region specified and obtains the key details and stores them in
|
||||
* this landscape persistently, such that {@link #getKeyPairInfo(Region, String)} as well as
|
||||
* {@link #getSSHKeyPair(Region, String)} will be able to obtain (information on) the key.
|
||||
* {@link #getSSHKeyPair(Region, String)} will be able to obtain (information on) the key. The private key is
|
||||
* stored encrypted with the passphrase provided as parameter {@code privateKeyEncryptionPassphrase}.
|
||||
*
|
||||
* @return the key ID as string, usually starting with the prefix "key-"
|
||||
*/
|
||||
SSHKeyPair createKeyPair(Region region, String keyName) throws JSchException;
|
||||
SSHKeyPair createKeyPair(Region region, String keyName, byte[] privateKeyEncryptionPassphrase) throws JSchException;
|
||||
|
||||
Instance getInstance(String instanceId, Region region);
|
||||
|
||||
@@ -487,19 +496,19 @@ extends Landscape<ShardingKey, MetricsT, ProcessT> {
|
||||
/**
|
||||
* Obtains all {@link #getApplicationProcessHostsByTag(Region, String, BiFunction) hosts} with a tag whose key is
|
||||
* specified by {@code tagName} and discovers all application server processes configured on it. These are then
|
||||
* grouped by {@link ApplicationProcess#getServerName(Optional) server name}, and using
|
||||
* grouped by {@link ApplicationProcess#getServerName(Optional, byte[]) server name}, and using
|
||||
* {@link ApplicationProcess#getMasterServerName(Optional)} the master/replica relationships between the processes with equal server
|
||||
* name are discovered. From this, an {@link ApplicationReplicaSet} is established per server name.
|
||||
*
|
||||
* @param processFactoryFromHostAndServerDirectory
|
||||
* takes the host and the server directory as arguments and is expected to produce an
|
||||
* {@link ApplicationProcess} object of some sort.
|
||||
* @param optionalTimeout
|
||||
* an optional timeout for communicating with the application server(s) to try to read the application
|
||||
* configuration; used, e.g., as timeout during establishing SSH connections
|
||||
* @param privateKeyEncryptionPassphrase TODO
|
||||
*/
|
||||
Iterable<ApplicationReplicaSet<ShardingKey, MetricsT, ProcessT>> getApplicationReplicaSetsByTag(Region region,
|
||||
String tagName, BiFunction<Host, String, ProcessT> processFactoryFromHostAndServerDirectory,
|
||||
Optional<Duration> optionalTimeout) throws Exception;
|
||||
Optional<Duration> optionalTimeout, byte[] privateKeyEncryptionPassphrase) throws Exception;
|
||||
|
||||
}
|
||||
@@ -52,29 +52,33 @@ public interface ReverseProxy<ShardingKey, MetricsT extends ApplicationProcessMe
|
||||
/**
|
||||
* Configures a redirect in this reverse proxy such that requests for it will go to the
|
||||
* {@code /index.html} landing page for the application replica set provided.
|
||||
* @param privateKeyEncryptionPassphrase TODO
|
||||
*/
|
||||
void setPlainRedirect(String hostname, ProcessT applicationProcess) throws InterruptedException, JSchException, IOException;
|
||||
void setPlainRedirect(String hostname, ProcessT applicationProcess, byte[] privateKeyEncryptionPassphrase) throws InterruptedException, JSchException, IOException;
|
||||
|
||||
/**
|
||||
* Configures a redirect in this reverse proxy such that requests for it will go to the
|
||||
* {@code /gwt/Home.html} landing page for the application replica set provided.
|
||||
* @param privateKeyEncryptionPassphrase TODO
|
||||
*/
|
||||
void setHomeRedirect(String hostname, ProcessT applicationProcess) throws InterruptedException, JSchException, IOException;
|
||||
void setHomeRedirect(String hostname, ProcessT applicationProcess, byte[] privateKeyEncryptionPassphrase) throws InterruptedException, JSchException, IOException;
|
||||
|
||||
/**
|
||||
* Configures a redirect in this reverse proxy such that requests for it will go to the
|
||||
* event page for the event with ID {@code eventId} that is expected to be hosted by the
|
||||
* application replica set provided.
|
||||
* @param privateKeyEncryptionPassphrase TODO
|
||||
*/
|
||||
void setEventRedirect(String hostname, ProcessT applicationProcess, UUID eventId) throws InterruptedException, JSchException, IOException;
|
||||
void setEventRedirect(String hostname, ProcessT applicationProcess, UUID eventId, byte[] privateKeyEncryptionPassphrase) throws InterruptedException, JSchException, IOException;
|
||||
|
||||
/**
|
||||
* Configures a redirect in this reverse proxy such that requests for it will go to the event series page for the
|
||||
* event series identified by the UUID of the leaderboard group that represents the series and which is expected to
|
||||
* be hosted by the application replica set provided.
|
||||
* @param privateKeyEncryptionPassphrase TODO
|
||||
*/
|
||||
void setEventSeriesRedirect(String hostname, ProcessT applicationProcess,
|
||||
UUID leaderboardGroupId) throws InterruptedException, JSchException, IOException;
|
||||
UUID leaderboardGroupId, byte[] privateKeyEncryptionPassphrase) throws InterruptedException, JSchException, IOException;
|
||||
|
||||
/**
|
||||
* Configures a rule for requests for anything from within {@code scope} such that those requests
|
||||
@@ -84,16 +88,18 @@ public interface ReverseProxy<ShardingKey, MetricsT extends ApplicationProcessMe
|
||||
|
||||
/**
|
||||
* Creates a mapping for the {@code /internal-server-status} path using the host's generic external ec2 host name
|
||||
* @param privateKeyEncryptionPassphrase TODO
|
||||
*/
|
||||
void createInternalStatusRedirect(Optional<Duration> optionalTimeout) throws InterruptedException, JSchException, IOException;
|
||||
void createInternalStatusRedirect(Optional<Duration> optionalTimeout, byte[] privateKeyEncryptionPassphrase) throws InterruptedException, JSchException, IOException;
|
||||
|
||||
/**
|
||||
* Removes any existing redirect mapping for the {@code hostname} provided. If no such mapping
|
||||
* exists, the method does nothing.
|
||||
* @param privateKeyEncryptionPassphrase TODO
|
||||
*/
|
||||
void removeRedirect(String hostname) throws InterruptedException, JSchException, IOException;
|
||||
void removeRedirect(String hostname, byte[] privateKeyEncryptionPassphrase) throws InterruptedException, JSchException, IOException;
|
||||
|
||||
void removeRedirect(Scope<ShardingKey> scope) throws IOException, InterruptedException, JSchException;
|
||||
void removeRedirect(Scope<ShardingKey> scope, byte[] privateKeyEncryptionPassphrase) throws IOException, InterruptedException, JSchException;
|
||||
|
||||
/**
|
||||
* {@link AwsLandscape#terminate(AwsInstance) Terminates} all {@link #getHosts() hosts} that form this reverse
|
||||
|
||||
+37
-26
@@ -80,15 +80,21 @@ implements com.sap.sse.landscape.Process<RotatingFileBasedLog, MetricsT> {
|
||||
return hostname+CONFIG_FILE_EXTENSION;
|
||||
}
|
||||
|
||||
private void setRedirect(String configFileNameForHostname, String macroName, String hostname, String... macroArguments) throws InterruptedException, JSchException, IOException {
|
||||
final String command = "echo \"Use "+macroName+" "+hostname+" "+String.join(" ", macroArguments)+"\" >"+getConfigFilePath(configFileNameForHostname)+
|
||||
"; service httpd reload";
|
||||
logger.info("Standard output from setting up the re-direct for "+hostname+" and reloading the Apache httpd server: "+
|
||||
runCommandAndReturnStdoutAndStderr(command, "Standard error from setting up the re-direct for "+hostname+" and reloading the Apache httpd server: ", Level.INFO));
|
||||
private void setRedirect(String configFileNameForHostname, String macroName, String hostname,
|
||||
byte[] privateKeyEncryptionPassphrase, String... macroArguments)
|
||||
throws InterruptedException, JSchException, IOException {
|
||||
final String command = "echo \"Use " + macroName + " " + hostname + " " + String.join(" ", macroArguments)
|
||||
+ "\" >" + getConfigFilePath(configFileNameForHostname) + "; service httpd reload";
|
||||
logger.info("Standard output from setting up the re-direct for " + hostname
|
||||
+ " and reloading the Apache httpd server: "
|
||||
+ runCommandAndReturnStdoutAndStderr(command,
|
||||
"Standard error from setting up the re-direct for " + hostname
|
||||
+ " and reloading the Apache httpd server: ",
|
||||
Level.INFO, privateKeyEncryptionPassphrase));
|
||||
}
|
||||
|
||||
private String runCommandAndReturnStdoutAndStderr(String command, String stderrLogPrefix, Level stderrLogLevel) throws IOException, InterruptedException, JSchException {
|
||||
final SshCommandChannel sshChannel = getHost().createRootSshChannel(TIMEOUT);
|
||||
private String runCommandAndReturnStdoutAndStderr(String command, String stderrLogPrefix, Level stderrLogLevel, byte[] privateKeyEncryptionPassphrase) throws IOException, InterruptedException, JSchException {
|
||||
final SshCommandChannel sshChannel = getHost().createRootSshChannel(TIMEOUT, privateKeyEncryptionPassphrase);
|
||||
final String stdout = sshChannel.runCommandAndReturnStdoutAndLogStderr(command, stderrLogPrefix, stderrLogLevel);
|
||||
return stdout;
|
||||
}
|
||||
@@ -98,60 +104,65 @@ implements com.sap.sse.landscape.Process<RotatingFileBasedLog, MetricsT> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setScopeRedirect(Scope<ShardingKey> scope,ProcessT applicationReplicaSet) {
|
||||
public void setScopeRedirect(Scope<ShardingKey> scope, ProcessT applicationReplicaSet) {
|
||||
// TODO Implement ApacheReverseProxy.setScopeRedirect(...)
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlainRedirect(String hostname, ProcessT applicationProcess) throws InterruptedException, JSchException, IOException {
|
||||
public void setPlainRedirect(String hostname, ProcessT applicationProcess, byte[] privateKeyEncryptionPassphrase) throws InterruptedException, JSchException, IOException {
|
||||
final String host = applicationProcess.getHost().getPrivateAddress().getHostAddress();
|
||||
final int port = applicationProcess.getPort();
|
||||
setRedirect(getConfigFileNameForHostname(hostname), PLAIN_REDIRECT_MACRO, hostname, host, ""+port);
|
||||
setRedirect(getConfigFileNameForHostname(hostname), PLAIN_REDIRECT_MACRO, hostname, privateKeyEncryptionPassphrase, host, ""+port);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHomeRedirect(String hostname, ProcessT applicationProcess) throws InterruptedException, JSchException, IOException {
|
||||
public void setHomeRedirect(String hostname, ProcessT applicationProcess, byte[] privateKeyEncryptionPassphrase) throws InterruptedException, JSchException, IOException {
|
||||
final String host = applicationProcess.getHost().getPrivateAddress().getHostAddress();
|
||||
final int port = applicationProcess.getPort();
|
||||
setRedirect(getConfigFileNameForHostname(hostname), HOME_REDIRECT_MACRO, hostname, host, ""+port);
|
||||
setRedirect(getConfigFileNameForHostname(hostname), HOME_REDIRECT_MACRO, hostname, privateKeyEncryptionPassphrase, host, ""+port);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEventRedirect(String hostname, ProcessT applicationProcess, UUID eventId) throws InterruptedException, JSchException, IOException {
|
||||
public void setEventRedirect(String hostname, ProcessT applicationProcess, UUID eventId, byte[] privateKeyEncryptionPassphrase) throws InterruptedException, JSchException, IOException {
|
||||
final String host = applicationProcess.getHost().getPrivateAddress().getHostAddress();
|
||||
final int port = applicationProcess.getPort();
|
||||
setRedirect(getConfigFileNameForHostname(hostname), EVENT_REDIRECT_MACRO, hostname, eventId.toString(), host, ""+port);
|
||||
setRedirect(getConfigFileNameForHostname(hostname), EVENT_REDIRECT_MACRO, hostname, privateKeyEncryptionPassphrase, eventId.toString(), host, ""+port);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEventSeriesRedirect(String hostname, ProcessT applicationProcess,
|
||||
UUID leaderboardGroupId) throws InterruptedException, JSchException, IOException {
|
||||
UUID leaderboardGroupId, byte[] privateKeyEncryptionPassphrase) throws InterruptedException, JSchException, IOException {
|
||||
final String host = applicationProcess.getHost().getPrivateAddress().getHostAddress();
|
||||
final int port = applicationProcess.getPort();
|
||||
setRedirect(getConfigFileNameForHostname(hostname), SERIES_REDIRECT_MACRO, hostname, leaderboardGroupId.toString(), host, ""+port);
|
||||
setRedirect(getConfigFileNameForHostname(hostname), SERIES_REDIRECT_MACRO, hostname, privateKeyEncryptionPassphrase, leaderboardGroupId.toString(), host, ""+port);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createInternalStatusRedirect(Optional<Duration> optionalTimeout) throws InterruptedException, JSchException, IOException {
|
||||
setRedirect(CONFIG_FILE_FOR_INTERNALS, STATUS, getHost().getPublicAddress(optionalTimeout).getCanonicalHostName(), INTERNAL_SERVER_STATUS);
|
||||
public void createInternalStatusRedirect(Optional<Duration> optionalTimeout, byte[] privateKeyEncryptionPassphrase) throws InterruptedException, JSchException, IOException {
|
||||
setRedirect(CONFIG_FILE_FOR_INTERNALS, STATUS, getHost().getPublicAddress(optionalTimeout).getCanonicalHostName(), privateKeyEncryptionPassphrase, INTERNAL_SERVER_STATUS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeRedirect(Scope<ShardingKey> scope) throws IOException, InterruptedException, JSchException {
|
||||
public void removeRedirect(Scope<ShardingKey> scope, byte[] privateKeyEncryptionPassphrase) throws IOException, InterruptedException, JSchException {
|
||||
final String configFilePath = getConfigFilePath(getConfigFileNameForScope(scope));
|
||||
removeRedirect(configFilePath, scope.toString());
|
||||
removeRedirect(configFilePath, scope.toString(), privateKeyEncryptionPassphrase);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeRedirect(String hostname) throws IOException, InterruptedException, JSchException {
|
||||
public void removeRedirect(String hostname, byte[] privateKeyEncryptionPassphrase) throws IOException, InterruptedException, JSchException {
|
||||
final String configFilePath = getConfigFilePath(getConfigFileNameForHostname(hostname));
|
||||
removeRedirect(configFilePath, hostname);
|
||||
removeRedirect(configFilePath, hostname, privateKeyEncryptionPassphrase);
|
||||
}
|
||||
|
||||
private void removeRedirect(String configFilePath, String redirectNameForLogOutput) throws IOException, InterruptedException, JSchException {
|
||||
final String command = "rm "+configFilePath+"; service httpd reload";
|
||||
logger.info("Standard output from removing the re-direct for "+redirectNameForLogOutput+" and reloading the Apache httpd server: "+
|
||||
runCommandAndReturnStdoutAndStderr(command, "Standard error from removing the re-direct for "+redirectNameForLogOutput+" and reloading the Apache httpd server: ", Level.INFO));
|
||||
private void removeRedirect(String configFilePath, String redirectNameForLogOutput,
|
||||
byte[] privateKeyEncryptionPassphrase) throws IOException, InterruptedException, JSchException {
|
||||
final String command = "rm " + configFilePath + "; service httpd reload";
|
||||
logger.info("Standard output from removing the re-direct for " + redirectNameForLogOutput
|
||||
+ " and reloading the Apache httpd server: "
|
||||
+ runCommandAndReturnStdoutAndStderr(command,
|
||||
"Standard error from removing the re-direct for " + redirectNameForLogOutput
|
||||
+ " and reloading the Apache httpd server: ",
|
||||
Level.INFO, privateKeyEncryptionPassphrase));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+14
-14
@@ -90,32 +90,32 @@ implements ReverseProxyCluster<ShardingKey, MetricsT, ProcessT, RotatingFileBase
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlainRedirect(String hostname, ProcessT applicationProcess) throws InterruptedException, JSchException, IOException {
|
||||
public void setPlainRedirect(String hostname, ProcessT applicationProcess, byte[] privateKeyEncryptionPassphrase) throws InterruptedException, JSchException, IOException {
|
||||
for (final ApacheReverseProxy<ShardingKey, MetricsT, ProcessT> proxy : getReverseProxies()) {
|
||||
proxy.setPlainRedirect(hostname, applicationProcess);
|
||||
proxy.setPlainRedirect(hostname, applicationProcess, privateKeyEncryptionPassphrase);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHomeRedirect(String hostname, ProcessT applicationProcess) throws InterruptedException, JSchException, IOException {
|
||||
public void setHomeRedirect(String hostname, ProcessT applicationProcess, byte[] privateKeyEncryptionPassphrase) throws InterruptedException, JSchException, IOException {
|
||||
for (final ApacheReverseProxy<ShardingKey, MetricsT, ProcessT> proxy : getReverseProxies()) {
|
||||
proxy.setHomeRedirect(hostname, applicationProcess);
|
||||
proxy.setHomeRedirect(hostname, applicationProcess, privateKeyEncryptionPassphrase);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEventRedirect(String hostname, ProcessT applicationProcess,
|
||||
UUID eventId) throws InterruptedException, JSchException, IOException {
|
||||
UUID eventId, byte[] privateKeyEncryptionPassphrase) throws InterruptedException, JSchException, IOException {
|
||||
for (final ApacheReverseProxy<ShardingKey, MetricsT, ProcessT> proxy : getReverseProxies()) {
|
||||
proxy.setEventRedirect(hostname, applicationProcess, eventId);
|
||||
proxy.setEventRedirect(hostname, applicationProcess, eventId, privateKeyEncryptionPassphrase);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEventSeriesRedirect(String hostname, ProcessT applicationProcess,
|
||||
UUID leaderboardGroupId) throws InterruptedException, JSchException, IOException {
|
||||
UUID leaderboardGroupId, byte[] privateKeyEncryptionPassphrase) throws InterruptedException, JSchException, IOException {
|
||||
for (final ApacheReverseProxy<ShardingKey, MetricsT, ProcessT> proxy : getReverseProxies()) {
|
||||
proxy.setEventSeriesRedirect(hostname, applicationProcess, leaderboardGroupId);
|
||||
proxy.setEventSeriesRedirect(hostname, applicationProcess, leaderboardGroupId, privateKeyEncryptionPassphrase);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,23 +127,23 @@ implements ReverseProxyCluster<ShardingKey, MetricsT, ProcessT, RotatingFileBase
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createInternalStatusRedirect(Optional<Duration> optionalTimeout) throws InterruptedException, JSchException, IOException {
|
||||
public void createInternalStatusRedirect(Optional<Duration> optionalTimeout, byte[] privateKeyEncryptionPassphrase) throws InterruptedException, JSchException, IOException {
|
||||
for (final ApacheReverseProxy<ShardingKey, MetricsT, ProcessT> proxy : getReverseProxies()) {
|
||||
proxy.createInternalStatusRedirect(optionalTimeout);
|
||||
proxy.createInternalStatusRedirect(optionalTimeout, privateKeyEncryptionPassphrase);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeRedirect(String hostname) throws IOException, InterruptedException, JSchException {
|
||||
public void removeRedirect(String hostname, byte[] privateKeyEncryptionPassphrase) throws IOException, InterruptedException, JSchException {
|
||||
for (final ApacheReverseProxy<ShardingKey, MetricsT, ProcessT> proxy : getReverseProxies()) {
|
||||
proxy.removeRedirect(hostname);
|
||||
proxy.removeRedirect(hostname, privateKeyEncryptionPassphrase);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeRedirect(Scope<ShardingKey> scope) throws IOException, InterruptedException, JSchException {
|
||||
public void removeRedirect(Scope<ShardingKey> scope, byte[] privateKeyEncryptionPassphrase) throws IOException, InterruptedException, JSchException {
|
||||
for (final ApacheReverseProxy<ShardingKey, MetricsT, ProcessT> proxy : getReverseProxies()) {
|
||||
proxy.removeRedirect(scope);
|
||||
proxy.removeRedirect(scope, privateKeyEncryptionPassphrase);
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
-4
@@ -51,13 +51,18 @@ implements ApplicationProcessHost<ShardingKey, MetricsT, ProcessT> {
|
||||
|
||||
/**
|
||||
* The implementation scans the {@link ApplicationProcessHost#DEFAULT_SERVERS_PATH application server deployment
|
||||
* folder} for sub-folders. In those sub-folders, the configuration file is analyzed for the port number to instantiate
|
||||
* an {@link ApplicationProcess} object for each one.
|
||||
* folder} for sub-folders. In those sub-folders, the configuration file is analyzed for the port number to
|
||||
* instantiate an {@link ApplicationProcess} object for each one.
|
||||
*
|
||||
* @param privateKeyEncryptionPassphrase
|
||||
* the pass phrase for the private key that belongs to the instance's public key used for start-up
|
||||
*/
|
||||
@Override
|
||||
public Iterable<ProcessT> getApplicationProcesses(Optional<Duration> optionalTimeout) throws SftpException, JSchException, IOException, InterruptedException {
|
||||
public Iterable<ProcessT> getApplicationProcesses(Optional<Duration> optionalTimeout,
|
||||
byte[] privateKeyEncryptionPassphrase)
|
||||
throws SftpException, JSchException, IOException, InterruptedException {
|
||||
final Set<ProcessT> result = new HashSet<>();
|
||||
final ChannelSftp sftpChannel = createRootSftpChannel(optionalTimeout);
|
||||
final ChannelSftp sftpChannel = createRootSftpChannel(optionalTimeout, privateKeyEncryptionPassphrase);
|
||||
if (optionalTimeout.isPresent()) {
|
||||
sftpChannel.connect((int) optionalTimeout.get().asMillis());
|
||||
} else {
|
||||
|
||||
+32
-16
@@ -120,23 +120,27 @@ public class AwsInstanceImpl<ShardingKey, MetricsT extends ApplicationProcessMet
|
||||
/**
|
||||
* Establishes an unconnected session configured for the "root" user.
|
||||
*
|
||||
* @param privateKeyEncryptionPassphrase
|
||||
* the pass phrase for the private key that belongs to the instance's public key used for start-up
|
||||
* @see #createRootSshChannel
|
||||
*/
|
||||
public com.jcraft.jsch.Session createRootSshSession() throws JSchException {
|
||||
return createSshSession(ROOT_USER_NAME);
|
||||
public com.jcraft.jsch.Session createRootSshSession(byte[] privateKeyEncryptionPassphrase) throws JSchException {
|
||||
return createSshSession(ROOT_USER_NAME, privateKeyEncryptionPassphrase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes an unconnected session configured for the "root" user.
|
||||
*
|
||||
* @param privateKeyEncryptionPassphrase
|
||||
* the pass phrase for the private key that belongs to the instance's public key used for start-up
|
||||
* @see #createRootSshChannel
|
||||
*/
|
||||
public com.jcraft.jsch.Session createSshSession(String sshUserName) throws JSchException {
|
||||
public com.jcraft.jsch.Session createSshSession(String sshUserName, byte[] privateKeyEncryptionPassphrase) throws JSchException {
|
||||
final String keyName = getInstance().keyName(); // the SSH key pair name that can be used to log on
|
||||
final SSHKeyPair keyPair = landscape.getSSHKeyPair(getRegion(), keyName);
|
||||
final JSch jsch = new JSch();
|
||||
JSch.setLogger(new JCraftLogAdapter());
|
||||
jsch.addIdentity(keyName, landscape.getDecryptedPrivateKey(keyPair), keyPair.getPublicKey(), /* passphrase */ null);
|
||||
jsch.addIdentity(keyName, landscape.getDecryptedPrivateKey(keyPair, privateKeyEncryptionPassphrase), keyPair.getPublicKey(), /* passphrase */ null);
|
||||
final InetAddress address = getPublicAddress();
|
||||
if (address == null) {
|
||||
throw new IllegalStateException("Instance "+getInstanceId()+" doesn't have a public IP address");
|
||||
@@ -146,12 +150,15 @@ public class AwsInstanceImpl<ShardingKey, MetricsT extends ApplicationProcessMet
|
||||
|
||||
/**
|
||||
* Connects to an SSH session for the "root" user with a "shell" channel
|
||||
* @param privateKeyEncryptionPassphrase
|
||||
* the pass phrase for the private key that belongs to the instance's public key used for start-up
|
||||
*
|
||||
* @see #createSshChannel(String, Optional)
|
||||
* @see #createSshChannel(String, Optional, byte[])
|
||||
*/
|
||||
@Override
|
||||
public SshCommandChannel createRootSshChannel(Optional<Duration> optionalTimeout) throws JSchException, IOException, InterruptedException {
|
||||
return createSshChannel(ROOT_USER_NAME, optionalTimeout);
|
||||
public SshCommandChannel createRootSshChannel(Optional<Duration> optionalTimeout,
|
||||
byte[] privateKeyEncryptionPassphrase) throws JSchException, IOException, InterruptedException {
|
||||
return createSshChannel(ROOT_USER_NAME, optionalTimeout, privateKeyEncryptionPassphrase);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -161,22 +168,28 @@ public class AwsInstanceImpl<ShardingKey, MetricsT extends ApplicationProcessMet
|
||||
* output stream} to which the server will send its output. You will usually want to use either a
|
||||
* {@link ByteArrayInputStream} to provide a set of predefined commands to sent to the server, and a
|
||||
* {@link PipedInputStream} wrapped around a {@link PipedOutputStream} which you set to the channel.
|
||||
*
|
||||
* @param privateKeyEncryptionPassphrase
|
||||
* the pass phrase for the private key that belongs to the instance's public key used for start-up
|
||||
*/
|
||||
@Override
|
||||
public SshCommandChannel createSshChannel(String sshUserName, Optional<Duration> optionalTimeout) throws JSchException, IOException, InterruptedException {
|
||||
return new SshCommandChannelImpl((ChannelExec) createSshChannelInternal(sshUserName, "exec", optionalTimeout));
|
||||
public SshCommandChannel createSshChannel(String sshUserName, Optional<Duration> optionalTimeout,
|
||||
byte[] privateKeyEncryptionPassphrase) throws JSchException, IOException, InterruptedException {
|
||||
return new SshCommandChannelImpl((ChannelExec) createSshChannelInternal(sshUserName, "exec", optionalTimeout, privateKeyEncryptionPassphrase));
|
||||
|
||||
}
|
||||
|
||||
private Channel createSshChannelInternal(String sshUserName, String channelType, Optional<Duration> optionalTimeout) throws JSchException, IOException, InterruptedException {
|
||||
private Channel createSshChannelInternal(String sshUserName, String channelType, Optional<Duration> optionalTimeout,
|
||||
byte[] privateKeyEncryptionPassphrase) throws JSchException, IOException, InterruptedException {
|
||||
final TimePoint start = TimePoint.now();
|
||||
logger.info("Creating SSH "+channelType+" channel for SSH user "+sshUserName+
|
||||
logger.info(
|
||||
"Creating SSH "+channelType+" channel for SSH user "+sshUserName+
|
||||
" to instance with ID "+getInstanceId());
|
||||
Channel channel = null;
|
||||
while (channel == null && (!optionalTimeout.isPresent() || start.until(TimePoint.now()).compareTo(optionalTimeout.get()) < 0)) {
|
||||
Session session = null;
|
||||
try {
|
||||
session = createSshSession(sshUserName);
|
||||
session = createSshSession(sshUserName, privateKeyEncryptionPassphrase);
|
||||
session.setUserInfo(new YesUserInfo());
|
||||
session.connect(optionalTimeout.map(d->d.asMillis()).orElse(0l).intValue());
|
||||
channel = session.openChannel(channelType);
|
||||
@@ -200,13 +213,16 @@ public class AwsInstanceImpl<ShardingKey, MetricsT extends ApplicationProcessMet
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelSftp createSftpChannel(String sshUserName, Optional<Duration> optionalTimeout) throws JSchException, IOException, InterruptedException {
|
||||
return (ChannelSftp) createSshChannelInternal(sshUserName, "sftp", optionalTimeout);
|
||||
public ChannelSftp createSftpChannel(String sshUserName, Optional<Duration> optionalTimeout,
|
||||
byte[] privateKeyEncryptionPassphrase) throws JSchException, IOException, InterruptedException {
|
||||
return (ChannelSftp) createSshChannelInternal(sshUserName, "sftp", optionalTimeout,
|
||||
privateKeyEncryptionPassphrase);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelSftp createRootSftpChannel(Optional<Duration> optionalTimeout) throws JSchException, IOException, InterruptedException {
|
||||
return createSftpChannel(ROOT_USER_NAME, optionalTimeout);
|
||||
public ChannelSftp createRootSftpChannel(Optional<Duration> optionalTimeout, byte[] privateKeyEncryptionPassphrase)
|
||||
throws JSchException, IOException, InterruptedException {
|
||||
return createSftpChannel(ROOT_USER_NAME, optionalTimeout, privateKeyEncryptionPassphrase);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+9
-15
@@ -157,12 +157,6 @@ implements AwsLandscape<ShardingKey, MetricsT, ProcessT> {
|
||||
private ConcurrentMap<Pair<String, String>, SSHKeyPair> sshKeyPairs;
|
||||
private final AwsRegion globalRegion;
|
||||
|
||||
/**
|
||||
* Used for the symmetric encryption / decryption of private SSH keys. See also
|
||||
* {@link #getDecryptedPrivateKey(SSHKeyPair)}.
|
||||
*/
|
||||
private final byte[] privateKeyEncryptionPassphrase;
|
||||
|
||||
public AwsLandscapeImpl() {
|
||||
this(System.getProperty(ACCESS_KEY_ID_SYSTEM_PROPERTY_NAME),
|
||||
System.getProperty(SECRET_ACCESS_KEY_SYSTEM_PROPERTY_NAME));
|
||||
@@ -177,7 +171,6 @@ implements AwsLandscape<ShardingKey, MetricsT, ProcessT> {
|
||||
|
||||
public AwsLandscapeImpl(String accessKeyId, String secretAccessKey,
|
||||
DomainObjectFactory domainObjectFactory, MongoObjectFactory mongoObjectFactory) {
|
||||
this.privateKeyEncryptionPassphrase = ("aw4raif87l"+"098sf;;50").getBytes();
|
||||
this.accessKeyId = accessKeyId;
|
||||
this.secretAccessKey = secretAccessKey;
|
||||
this.globalRegion = new AwsRegion(Region.AWS_GLOBAL);
|
||||
@@ -203,7 +196,8 @@ implements AwsLandscape<ShardingKey, MetricsT, ProcessT> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSSHKeyPair(com.sap.sse.landscape.Region region, String creator, String keyName, KeyPair keyPairWithDecryptedPrivateKey) {
|
||||
public void addSSHKeyPair(com.sap.sse.landscape.Region region, String creator, String keyName, KeyPair keyPairWithDecryptedPrivateKey) throws JSchException {
|
||||
assert !keyPairWithDecryptedPrivateKey.isEncrypted();
|
||||
addSSHKeyPair(new SSHKeyPair(region.getId(), creator, TimePoint.now(), keyName, keyPairWithDecryptedPrivateKey.getPublicKeyBlob(),
|
||||
getPrivateKeyBytes(keyPairWithDecryptedPrivateKey)));
|
||||
}
|
||||
@@ -214,7 +208,7 @@ implements AwsLandscape<ShardingKey, MetricsT, ProcessT> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSHKeyPair createKeyPair(com.sap.sse.landscape.Region region, String keyName) throws JSchException {
|
||||
public SSHKeyPair createKeyPair(com.sap.sse.landscape.Region region, String keyName, byte[] privateKeyEncryptionPassphrase) throws JSchException {
|
||||
final CreateKeyPairResponse keyPairResponse = getEc2Client(getRegion(region))
|
||||
.createKeyPair(CreateKeyPairRequest.builder().keyName(keyName).build());
|
||||
final String keyMaterial = keyPairResponse.keyMaterial();
|
||||
@@ -609,7 +603,7 @@ implements AwsLandscape<ShardingKey, MetricsT, ProcessT> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String importKeyPair(com.sap.sse.landscape.Region region, byte[] publicKey, byte[] unencryptedPrivateKey, String keyName) throws JSchException {
|
||||
public String importKeyPair(com.sap.sse.landscape.Region region, byte[] publicKey, byte[] encryptedPrivateKey, String keyName) throws JSchException {
|
||||
final String keyId = getEc2Client(getRegion(region)).importKeyPair(ImportKeyPairRequest.builder().keyName(keyName)
|
||||
.publicKeyMaterial(SdkBytes.fromByteArray(publicKey)).build()).keyPairId();
|
||||
Object principal;
|
||||
@@ -620,7 +614,7 @@ implements AwsLandscape<ShardingKey, MetricsT, ProcessT> {
|
||||
principal = null;
|
||||
}
|
||||
final SSHKeyPair keyPair = new SSHKeyPair(region.getId(), principal==null?"":principal.toString(),
|
||||
TimePoint.now(), keyName, publicKey, unencryptedPrivateKey, privateKeyEncryptionPassphrase);
|
||||
TimePoint.now(), keyName, publicKey, encryptedPrivateKey);
|
||||
addSSHKeyPair(keyPair);
|
||||
return keyId;
|
||||
}
|
||||
@@ -631,7 +625,7 @@ implements AwsLandscape<ShardingKey, MetricsT, ProcessT> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getDecryptedPrivateKey(SSHKeyPair keyPair) throws JSchException {
|
||||
public byte[] getDecryptedPrivateKey(SSHKeyPair keyPair, byte[] privateKeyEncryptionPassphrase) throws JSchException {
|
||||
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
keyPair.getKeyPair(new JSch(), privateKeyEncryptionPassphrase).writePrivateKey(bos);
|
||||
return bos.toByteArray();
|
||||
@@ -1033,13 +1027,13 @@ implements AwsLandscape<ShardingKey, MetricsT, ProcessT> {
|
||||
|
||||
@Override
|
||||
public Iterable<ApplicationReplicaSet<ShardingKey, MetricsT, ProcessT>> getApplicationReplicaSetsByTag(com.sap.sse.landscape.Region region, String tagName,
|
||||
BiFunction<Host, String, ProcessT> processFactoryFromHostAndServerDirectory, Optional<Duration> optionalTimeout) throws Exception {
|
||||
BiFunction<Host, String, ProcessT> processFactoryFromHostAndServerDirectory, Optional<Duration> optionalTimeout, byte[] privateKeyEncryptionPassphrase) throws Exception {
|
||||
final Iterable<ApplicationProcessHost<ShardingKey, MetricsT, ProcessT>> hosts = getApplicationProcessHostsByTag(region, tagName, processFactoryFromHostAndServerDirectory);
|
||||
final Map<String, ProcessT> mastersByServerName = new HashMap<>();
|
||||
final Map<String, Set<ProcessT>> replicasByServerName = new HashMap<>();
|
||||
for (final ApplicationProcessHost<ShardingKey, MetricsT, ProcessT> host : hosts) {
|
||||
for (final ProcessT applicationProcess : host.getApplicationProcesses(optionalTimeout)) {
|
||||
final String serverName = applicationProcess.getServerName(optionalTimeout);
|
||||
for (final ProcessT applicationProcess : host.getApplicationProcesses(optionalTimeout, privateKeyEncryptionPassphrase)) {
|
||||
final String serverName = applicationProcess.getServerName(optionalTimeout, privateKeyEncryptionPassphrase);
|
||||
final String masterServerName = applicationProcess.getMasterServerName(optionalTimeout);
|
||||
if (masterServerName != null && Util.equalsWithNull(masterServerName, serverName)) {
|
||||
// then applicationProcess is a replica in the serverName cluster:
|
||||
|
||||
+9
-1
@@ -101,6 +101,7 @@ extends ProcedureWithTargetGroup<ShardingKey, MetricsT, ProcessT, HostT> {
|
||||
BuilderT setProcess(ProcessT process);
|
||||
BuilderT setHostname(String hostname);
|
||||
BuilderT setTimeout(Duration timeout);
|
||||
BuilderT setPrivateKeyEncryptionPassphrase(byte[] privateKeyEncryptionPassphrase);
|
||||
}
|
||||
|
||||
protected abstract static class BuilderImpl<BuilderT extends Builder<BuilderT, T, ShardingKey, MetricsT, ProcessT, HostT>,
|
||||
@@ -113,6 +114,7 @@ extends ProcedureWithTargetGroup<ShardingKey, MetricsT, ProcessT, HostT> {
|
||||
private String hostname;
|
||||
private ProcessT process;
|
||||
private Optional<Duration> optionalTimeout = Optional.empty();
|
||||
private byte[] privateKeyEncryptionPassphrase;
|
||||
|
||||
@Override
|
||||
public BuilderT setProcess(ProcessT process) {
|
||||
@@ -143,6 +145,12 @@ extends ProcedureWithTargetGroup<ShardingKey, MetricsT, ProcessT, HostT> {
|
||||
protected Optional<Duration> getOptionalTimeout() {
|
||||
return optionalTimeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderT setPrivateKeyEncryptionPassphrase(byte[] privateKeyEncryptionPassphrase) {
|
||||
this.privateKeyEncryptionPassphrase = privateKeyEncryptionPassphrase;
|
||||
return self();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getServerName() throws Exception {
|
||||
@@ -150,7 +158,7 @@ extends ProcedureWithTargetGroup<ShardingKey, MetricsT, ProcessT, HostT> {
|
||||
if (super.getServerName() != null) {
|
||||
result = super.getServerName();
|
||||
} else {
|
||||
result = getProcess().getServerName(getOptionalTimeout());
|
||||
result = getProcess().getServerName(getOptionalTimeout(), privateKeyEncryptionPassphrase);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
+31
-1
@@ -16,6 +16,7 @@ import com.jcraft.jsch.JSchException;
|
||||
import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.landscape.AvailabilityZone;
|
||||
import com.sap.sse.landscape.Host;
|
||||
import com.sap.sse.landscape.Landscape;
|
||||
import com.sap.sse.landscape.ProcessConfigurationVariable;
|
||||
import com.sap.sse.landscape.Region;
|
||||
@@ -56,6 +57,7 @@ extends StartHost<ShardingKey, MetricsT, ProcessT, HostT> {
|
||||
private final Iterable<SecurityGroup> securityGroups;
|
||||
private final Optional<Tags> tags;
|
||||
private final HostSupplier<ShardingKey, MetricsT, ProcessT, HostT> hostSupplier;
|
||||
private final byte[] privateKeyEncryptionPassphrase;
|
||||
private HostT host;
|
||||
|
||||
/**
|
||||
@@ -89,7 +91,19 @@ extends StartHost<ShardingKey, MetricsT, ProcessT, HostT> {
|
||||
|
||||
BuilderT setAvailabilityZone(AwsAvailabilityZone availabilityZone);
|
||||
|
||||
/**
|
||||
* Tells the name of the SSH key stored in the AWS landscape that is to be used to launch the instance; the
|
||||
* respective public key will be added so that an owner of the corresponding private key can
|
||||
* {@link Host#createSshChannel(String, Optional, byte[]) SSH} into the resulting host.<p>
|
||||
*
|
||||
* Make sure to also provide the {@link #setPrivateKeyEncryptionPassphrase(byte[]) private key pass phrase}
|
||||
* that is required to decrypt the pass phrase of the private key belonging to the public key identified
|
||||
* by {@code keyName}. This private key is expected to be stored together with the entire key pair in the
|
||||
* "landscape" persistently.
|
||||
*/
|
||||
BuilderT setKeyName(String keyName);
|
||||
|
||||
BuilderT setPrivateKeyEncryptionPassphrase(byte[] privateKeyEncryptionPassphrase);
|
||||
|
||||
BuilderT setSecurityGroups(Iterable<SecurityGroup> securityGroups);
|
||||
|
||||
@@ -120,6 +134,7 @@ extends StartHost<ShardingKey, MetricsT, ProcessT, HostT> {
|
||||
private AwsRegion region;
|
||||
private String instanceName;
|
||||
private HostSupplier<ShardingKey, MetricsT, ProcessT, HostT> hostSupplier;
|
||||
private byte[] privateKeyEncryptionPassphrase;
|
||||
|
||||
protected AwsLandscape<ShardingKey, MetricsT, ProcessT> getLandscape() {
|
||||
return (AwsLandscape<ShardingKey, MetricsT, ProcessT>) super.getLandscape();
|
||||
@@ -233,6 +248,16 @@ extends StartHost<ShardingKey, MetricsT, ProcessT, HostT> {
|
||||
this.hostSupplier = hostSupplier;
|
||||
return self();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderT setPrivateKeyEncryptionPassphrase(byte[] privateKeyEncryptionPassphrase) {
|
||||
this.privateKeyEncryptionPassphrase = privateKeyEncryptionPassphrase;
|
||||
return self();
|
||||
}
|
||||
|
||||
protected byte[] getPrivateKeyEncryptionPassphrase() {
|
||||
return privateKeyEncryptionPassphrase;
|
||||
}
|
||||
}
|
||||
|
||||
protected StartAwsHost(BuilderImpl<?, ? extends StartAwsHost<ShardingKey, MetricsT, ProcessT, HostT>, ShardingKey, MetricsT, ProcessT, HostT> builder) {
|
||||
@@ -247,6 +272,7 @@ extends StartHost<ShardingKey, MetricsT, ProcessT, HostT> {
|
||||
this.tags = Optional.of(builder.getTags().orElse(Tags.empty()).and(NAME_TAG_NAME, builder.getInstanceName()));
|
||||
this.securityGroups = builder.getSecurityGroups();
|
||||
this.hostSupplier = builder.getHostSupplier();
|
||||
this.privateKeyEncryptionPassphrase = builder.getPrivateKeyEncryptionPassphrase();
|
||||
}
|
||||
|
||||
protected static <ShardingKey,
|
||||
@@ -350,10 +376,14 @@ extends StartHost<ShardingKey, MetricsT, ProcessT, HostT> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected byte[] getPrivateKeyEncryptionPassphrase() {
|
||||
return privateKeyEncryptionPassphrase;
|
||||
}
|
||||
|
||||
protected void copyRootAuthorizedKeysToOtherUser(String username, Optional<Duration> optionalTimeout)
|
||||
throws JSchException, IOException, InterruptedException {
|
||||
final SshCommandChannel sshChannel = getHost().createRootSshChannel(optionalTimeout);
|
||||
final SshCommandChannel sshChannel = getHost().createRootSshChannel(optionalTimeout, getPrivateKeyEncryptionPassphrase());
|
||||
final String sailingUserSsh = "/home/" + username + "/.ssh";
|
||||
final String sailingUserAuthorizedKeys = sailingUserSsh + "/authorized_keys";
|
||||
logger.info("Appended root's authorized_keys also to " + username + "'s authorized_keys. stdout: "
|
||||
|
||||
-2
@@ -259,6 +259,4 @@ extends StartAwsHost<ShardingKey, MetricsT, ProcessT, AwsInstance<ShardingKey, M
|
||||
mongoProcess = new MongoProcessInReplicaSetImpl(replicaSet, MongoProcess.DEFAULT_PORT, instance);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -56,19 +56,33 @@ public interface Host {
|
||||
* output. You will usually want to use either a {@link ByteArrayInputStream} to provide a set of predefined
|
||||
* commands to sent to the server, and a {@link PipedInputStream} wrapped around a {@link PipedOutputStream} which
|
||||
* you set to the channel.
|
||||
*
|
||||
* @param privateKeyEncryptionPassphrase
|
||||
* the pass phrase for the private key that belongs to the instance's public key used for start-up
|
||||
*/
|
||||
SshCommandChannel createSshChannel(String sshUserName, Optional<Duration> optionalTimeout) throws JSchException, IOException, InterruptedException;
|
||||
SshCommandChannel createSshChannel(String sshUserName, Optional<Duration> optionalTimeout, byte[] privateKeyEncryptionPassphrase) throws JSchException, IOException, InterruptedException;
|
||||
|
||||
/**
|
||||
* Connects to an SSH session for the "root" user with a "shell" channel
|
||||
*
|
||||
* @see #createSshChannel(String, Optional)
|
||||
* @param privateKeyEncryptionPassphrase
|
||||
* the pass phrase for the private key that belongs to the instance's public key used for start-up
|
||||
*
|
||||
* @see #createSshChannel(String, Optional, byte[])
|
||||
*/
|
||||
SshCommandChannel createRootSshChannel(Optional<Duration> optionalTimeout) throws JSchException, IOException, InterruptedException;
|
||||
|
||||
ChannelSftp createSftpChannel(String sshUserName, Optional<Duration> optionalTimeout) throws JSchException, IOException, InterruptedException;
|
||||
SshCommandChannel createRootSshChannel(Optional<Duration> optionalTimeout, byte[] privateKeyEncryptionPassphrase) throws JSchException, IOException, InterruptedException;
|
||||
|
||||
ChannelSftp createRootSftpChannel(Optional<Duration> optionalTimeout) throws JSchException, IOException, InterruptedException;
|
||||
/**
|
||||
* @param privateKeyEncryptionPassphrase
|
||||
* the pass phrase for the private key that belongs to the instance's public key used for start-up
|
||||
*/
|
||||
ChannelSftp createSftpChannel(String sshUserName, Optional<Duration> optionalTimeout, byte[] privateKeyEncryptionPassphrase) throws JSchException, IOException, InterruptedException;
|
||||
|
||||
/**
|
||||
* @param privateKeyEncryptionPassphrase
|
||||
* the pass phrase for the private key that belongs to the instance's public key used for start-up
|
||||
*/
|
||||
ChannelSftp createRootSftpChannel(Optional<Duration> optionalTimeout, byte[] privateKeyEncryptionPassphrase) throws JSchException, IOException, InterruptedException;
|
||||
|
||||
/**
|
||||
* Tells where in the cloud this host runs; the availability zone {@link AvailabilityZone#getRegion() implies} the
|
||||
|
||||
+10
-6
@@ -29,9 +29,10 @@ extends Process<RotatingFileBasedLog, MetricsT> {
|
||||
* @param releaseRepository
|
||||
* mandatory parameter required to enable resolving the release and enabling the download of its
|
||||
* artifacts, including its release notes
|
||||
* @param privateKeyEncryptionPassphrase TODO
|
||||
* @return the release that this process is currently running
|
||||
*/
|
||||
Release getRelease(ReleaseRepository releaseRepository, Optional<Duration> optionalTimeout) throws JSchException, IOException, SftpException, InterruptedException;
|
||||
Release getRelease(ReleaseRepository releaseRepository, Optional<Duration> optionalTimeout, byte[] privateKeyEncryptionPassphrase) throws JSchException, IOException, SftpException, InterruptedException;
|
||||
|
||||
/**
|
||||
* Tries to shut down an OSGi application server process cleanly by sending the "shutdown" OSGi command to this
|
||||
@@ -48,7 +49,7 @@ extends Process<RotatingFileBasedLog, MetricsT> {
|
||||
*/
|
||||
boolean tryCleanShutdown(Duration timeout, boolean forceAfterTimeout);
|
||||
|
||||
int getTelnetPortToOSGiConsole(Optional<Duration> optionalTimeout) throws NumberFormatException, JSchException, IOException, SftpException, InterruptedException;
|
||||
int getTelnetPortToOSGiConsole(Optional<Duration> optionalTimeout, byte[] privateKeyEncryptionPassphrase) throws NumberFormatException, JSchException, IOException, SftpException, InterruptedException;
|
||||
|
||||
/**
|
||||
* @return the directory as an absolute path that can be used, e.g., in a {@link ChannelSftp} to change directory to
|
||||
@@ -62,10 +63,11 @@ extends Process<RotatingFileBasedLog, MetricsT> {
|
||||
* as the base name of the {@link #getServerDirectory() server's directory}. Often, it is also used as the name of
|
||||
* the {@link Database}, at least when this is a master node, and the name of the RabbitMQ fan-out exchange used
|
||||
* for replication.
|
||||
* @param privateKeyEncryptionPassphrase TODO
|
||||
*/
|
||||
String getServerName(Optional<Duration> optionalTimeout) throws Exception;
|
||||
String getServerName(Optional<Duration> optionalTimeout, byte[] privateKeyEncryptionPassphrase) throws Exception;
|
||||
|
||||
String getEnvSh(Optional<Duration> optionalTimeout) throws JSchException, IOException, SftpException, InterruptedException;
|
||||
String getEnvSh(Optional<Duration> optionalTimeout, byte[] privateKeyEncryptionPassphrase) throws JSchException, IOException, SftpException, InterruptedException;
|
||||
|
||||
/**
|
||||
* The URL path (everything following the hostname and starting with "/" but without any fragment) that can be
|
||||
@@ -80,15 +82,17 @@ extends Process<RotatingFileBasedLog, MetricsT> {
|
||||
/**
|
||||
* Obtains the last definition of the process configuration variable specified, or {@code null} if that variable cannot be found
|
||||
* in the evaluated {@code env.sh} file.
|
||||
* @param privateKeyEncryptionPassphrase TODO
|
||||
*/
|
||||
String getEnvShValueFor(ProcessConfigurationVariable variable, Optional<Duration> optionalTimeout)
|
||||
String getEnvShValueFor(ProcessConfigurationVariable variable, Optional<Duration> optionalTimeout, byte[] privateKeyEncryptionPassphrase)
|
||||
throws JSchException, IOException, InterruptedException;
|
||||
|
||||
/**
|
||||
* Obtains the last definition of the process configuration variable specified, or {@code null} if that variable isn't set
|
||||
* by evaluating the {@code env.sh} file on the {@link #getHost() host}.
|
||||
* @param privateKeyEncryptionPassphrase TODO
|
||||
*/
|
||||
String getEnvShValueFor(String variableName, Optional<Duration> optionalTimeout)
|
||||
String getEnvShValueFor(String variableName, Optional<Duration> optionalTimeout, byte[] privateKeyEncryptionPassphrase)
|
||||
throws JSchException, IOException, InterruptedException;
|
||||
|
||||
/**
|
||||
|
||||
+48
-30
@@ -45,16 +45,19 @@ implements ApplicationProcess<ShardingKey, MetricsT, ProcessT> {
|
||||
/**
|
||||
* Absolute path in the file system of the host on which this process is running and that represents
|
||||
* this process's working directory. This directory is expected to contain a file named {@link #ENV_SH}
|
||||
* whose contents can be obtained using the {@link #getEnvSh(Optional)} method.
|
||||
* whose contents can be obtained using the {@link #getEnvSh(Optional, byte[])} method.
|
||||
*/
|
||||
private final String serverDirectory;
|
||||
|
||||
/**
|
||||
* Alternative constructor that doesn't take the port number as argument but instead tries to obtain it from the {@link #ENV_SH env.sh} file
|
||||
* located on the {@code host} in the {@code serverDirectory} specified.
|
||||
* Alternative constructor that doesn't take the port number as argument but instead tries to obtain it from the
|
||||
* {@link #ENV_SH env.sh} file located on the {@code host} in the {@code serverDirectory} specified.
|
||||
*
|
||||
* @param privateKeyEncryptionPassphrase
|
||||
* the pass phrase for the private key that belongs to the instance's public key used for start-up
|
||||
*/
|
||||
public ApplicationProcessImpl(Host host, String serverDirectory, Optional<Duration> optionalTimeout) throws NumberFormatException, JSchException, IOException, InterruptedException {
|
||||
this(readPortFromDirectory(host, serverDirectory, optionalTimeout), host, serverDirectory);
|
||||
public ApplicationProcessImpl(Host host, String serverDirectory, Optional<Duration> optionalTimeout, byte[] privateKeyEncryptionPassphrase) throws NumberFormatException, JSchException, IOException, InterruptedException {
|
||||
this(readPortFromDirectory(host, serverDirectory, optionalTimeout, privateKeyEncryptionPassphrase), host, serverDirectory);
|
||||
}
|
||||
|
||||
public ApplicationProcessImpl(int port, Host host, String serverDirectory) {
|
||||
@@ -62,14 +65,20 @@ implements ApplicationProcess<ShardingKey, MetricsT, ProcessT> {
|
||||
this.serverDirectory = serverDirectory;
|
||||
}
|
||||
|
||||
private static int readPortFromDirectory(Host host, String serverDirectory, Optional<Duration> optionalTimeout) throws NumberFormatException, JSchException, IOException, InterruptedException {
|
||||
return Integer.parseInt(getEnvShValueFor(host, serverDirectory, DefaultProcessConfigurationVariables.SERVER_PORT.name(), optionalTimeout));
|
||||
private static int readPortFromDirectory(Host host, String serverDirectory, Optional<Duration> optionalTimeout,
|
||||
byte[] privateKeyEncryptionPassphrase)
|
||||
throws NumberFormatException, JSchException, IOException, InterruptedException {
|
||||
return Integer.parseInt(
|
||||
getEnvShValueFor(host, serverDirectory, DefaultProcessConfigurationVariables.SERVER_PORT.name(),
|
||||
optionalTimeout, privateKeyEncryptionPassphrase));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Release getRelease(ReleaseRepository releaseRepository, Optional<Duration> optionalTimeout) throws JSchException, IOException, SftpException, InterruptedException {
|
||||
public Release getRelease(ReleaseRepository releaseRepository, Optional<Duration> optionalTimeout,
|
||||
byte[] privateKeyEncryptionPassphrase)
|
||||
throws JSchException, IOException, SftpException, InterruptedException {
|
||||
final Pattern pattern = Pattern.compile("^[^-]*-([^ ]*) System:");
|
||||
final Matcher matcher = pattern.matcher(getVersionTxt(optionalTimeout));
|
||||
final Matcher matcher = pattern.matcher(getVersionTxt(optionalTimeout, privateKeyEncryptionPassphrase));
|
||||
final Release result;
|
||||
if (matcher.find()) {
|
||||
result = new ReleaseImpl(matcher.group(1), releaseRepository);
|
||||
@@ -80,14 +89,18 @@ implements ApplicationProcess<ShardingKey, MetricsT, ProcessT> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Under the path {@code configuration/jetty/version.txt} each release is expected to present a version string in the
|
||||
* format <tt>{commitId}-{releaseName} System: {mongoDbUriWithoutSchemaOrHostPortDBName}-{expeditionPort}-{replicationHost}:{replicationPort}/{replicationChannel} Started: {yyyyMMddHHmm}</tt>.
|
||||
* With this it is possible to infer the release that will be run upon the next process start, which is also the
|
||||
* one running now if this process is currently running and no other release has been deployed since the process
|
||||
* has started.
|
||||
* Under the path {@code configuration/jetty/version.txt} each release is expected to present a version string in
|
||||
* the format
|
||||
* <tt>{commitId}-{releaseName} System: {mongoDbUriWithoutSchemaOrHostPortDBName}-{expeditionPort}-{replicationHost}:{replicationPort}/{replicationChannel} Started: {yyyyMMddHHmm}</tt>.
|
||||
* With this it is possible to infer the release that will be run upon the next process start, which is also the one
|
||||
* running now if this process is currently running and no other release has been deployed since the process has
|
||||
* started.
|
||||
*
|
||||
* @param privateKeyEncryptionPassphrase
|
||||
* the pass phrase for the private key that belongs to the instance's public key used for start-up
|
||||
*/
|
||||
private String getVersionTxt(Optional<Duration> optionalTimeout) throws JSchException, IOException, SftpException, InterruptedException {
|
||||
return getFileContents(getServerDirectory()+"/"+VERSION_TXT, optionalTimeout);
|
||||
private String getVersionTxt(Optional<Duration> optionalTimeout, byte[] privateKeyEncryptionPassphrase) throws JSchException, IOException, SftpException, InterruptedException {
|
||||
return getFileContents(getServerDirectory()+"/"+VERSION_TXT, optionalTimeout, privateKeyEncryptionPassphrase);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -97,8 +110,10 @@ implements ApplicationProcess<ShardingKey, MetricsT, ProcessT> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTelnetPortToOSGiConsole(Optional<Duration> optionalTimeout) throws NumberFormatException, JSchException, IOException, SftpException, InterruptedException {
|
||||
return Integer.parseInt(getEnvShValueFor(DefaultProcessConfigurationVariables.TELNET_PORT, optionalTimeout));
|
||||
public int getTelnetPortToOSGiConsole(Optional<Duration> optionalTimeout, byte[] privateKeyEncryptionPassphrase)
|
||||
throws NumberFormatException, JSchException, IOException, SftpException, InterruptedException {
|
||||
return Integer.parseInt(getEnvShValueFor(DefaultProcessConfigurationVariables.TELNET_PORT, optionalTimeout,
|
||||
privateKeyEncryptionPassphrase));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -106,12 +121,15 @@ implements ApplicationProcess<ShardingKey, MetricsT, ProcessT> {
|
||||
* by evaluating the {@code env.sh} file on the {@link #getHost() host}.
|
||||
*/
|
||||
@Override
|
||||
public String getEnvShValueFor(String variableName, Optional<Duration> optionalTimeout) throws JSchException, IOException, InterruptedException {
|
||||
return getEnvShValueFor(getHost(), getServerDirectory(), variableName, optionalTimeout);
|
||||
public String getEnvShValueFor(String variableName, Optional<Duration> optionalTimeout,
|
||||
byte[] privateKeyEncryptionPassphrase) throws JSchException, IOException, InterruptedException {
|
||||
return getEnvShValueFor(getHost(), getServerDirectory(), variableName, optionalTimeout, privateKeyEncryptionPassphrase);
|
||||
}
|
||||
|
||||
protected static String getEnvShValueFor(Host host, String serverDirectory, String variableName, Optional<Duration> optionalTimeout) throws JSchException, IOException, InterruptedException {
|
||||
final SshCommandChannel sshChannel = host.createRootSshChannel(optionalTimeout);
|
||||
protected static String getEnvShValueFor(Host host, String serverDirectory, String variableName,
|
||||
Optional<Duration> optionalTimeout, byte[] privateKeyEncryptionPassphrase)
|
||||
throws JSchException, IOException, InterruptedException {
|
||||
final SshCommandChannel sshChannel = host.createRootSshChannel(optionalTimeout, privateKeyEncryptionPassphrase);
|
||||
final String variableValue = sshChannel.runCommandAndReturnStdoutAndLogStderr(". "+getEnvShPath(serverDirectory)+">/dev/null 2>/dev/null; "+
|
||||
"echo \"${"+variableName+"}\"", /* stderr prefix */ null, /* stderr log level */ null);
|
||||
return variableValue.endsWith("\n") ? variableValue.substring(0, variableValue.length()-1) : variableValue;
|
||||
@@ -122,8 +140,8 @@ implements ApplicationProcess<ShardingKey, MetricsT, ProcessT> {
|
||||
* in the evaluated {@code env.sh} file.
|
||||
*/
|
||||
@Override
|
||||
public String getEnvShValueFor(ProcessConfigurationVariable variable, Optional<Duration> optionalTimeout) throws JSchException, IOException, InterruptedException {
|
||||
return getEnvShValueFor(variable.name(), optionalTimeout);
|
||||
public String getEnvShValueFor(ProcessConfigurationVariable variable, Optional<Duration> optionalTimeout, byte[] privateKeyEncryptionPassphrase) throws JSchException, IOException, InterruptedException {
|
||||
return getEnvShValueFor(variable.name(), optionalTimeout, privateKeyEncryptionPassphrase);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -140,18 +158,18 @@ implements ApplicationProcess<ShardingKey, MetricsT, ProcessT> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServerName(Optional<Duration> optionalTimeout) throws Exception {
|
||||
return getEnvShValueFor(DefaultProcessConfigurationVariables.SERVER_NAME, optionalTimeout);
|
||||
public String getServerName(Optional<Duration> optionalTimeout, byte[] privateKeyEncryptionPassphrase) throws Exception {
|
||||
return getEnvShValueFor(DefaultProcessConfigurationVariables.SERVER_NAME, optionalTimeout, privateKeyEncryptionPassphrase);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEnvSh(Optional<Duration> optionalTimeout) throws JSchException, IOException, SftpException, InterruptedException {
|
||||
return getFileContents(getEnvShPath(), optionalTimeout);
|
||||
public String getEnvSh(Optional<Duration> optionalTimeout, byte[] privateKeyEncryptionPassphrase) throws JSchException, IOException, SftpException, InterruptedException {
|
||||
return getFileContents(getEnvShPath(), optionalTimeout, privateKeyEncryptionPassphrase);
|
||||
}
|
||||
|
||||
protected String getFileContents(String path, Optional<Duration> optionalTimeout)
|
||||
protected String getFileContents(String path, Optional<Duration> optionalTimeout, byte[] privateKeyEncryptionPassphrase)
|
||||
throws JSchException, IOException, SftpException, InterruptedException {
|
||||
final ChannelSftp sftpChannel = getHost().createRootSftpChannel(optionalTimeout);
|
||||
final ChannelSftp sftpChannel = getHost().createRootSftpChannel(optionalTimeout, privateKeyEncryptionPassphrase);
|
||||
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
sftpChannel.connect((int) optionalTimeout.orElse(Duration.NULL).asMillis());
|
||||
sftpChannel.get(path, bos);
|
||||
|
||||
@@ -8,6 +8,7 @@ import com.jcraft.jsch.KeyPair;
|
||||
import com.sap.sse.common.Named;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.impl.NamedImpl;
|
||||
import com.sap.sse.landscape.Host;
|
||||
|
||||
/**
|
||||
* Region ID and key name together form a unique key for AWS key pairs. The private key is always stored in an encrypted form,
|
||||
@@ -55,13 +56,19 @@ public class SSHKeyPair extends NamedImpl implements Named {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link KeyPair} with decrypted private key that can be used to initiate an SSH session, e.g., using
|
||||
* {@link Host#createSshChannel(String, java.util.Optional, byte[])}.
|
||||
*
|
||||
* @param passphrase
|
||||
* used to {@link KeyPair#decrypt(byte[]) decrypt} the encrypted private key so that the resulting key
|
||||
* pair can be used to initiate sessions.
|
||||
* pair can be used to initiate sessions. Has to equal the passphrase used when encrypting the private key,
|
||||
* e.g., when calling {@link #SSHKeyPair(String, String, TimePoint, String, byte[], byte[], byte[])}.
|
||||
*/
|
||||
public KeyPair getKeyPair(JSch jsch, byte[] passphrase) throws JSchException {
|
||||
final KeyPair result = KeyPair.load(jsch, getEncryptedPrivateKey(), getPublicKey());
|
||||
result.decrypt(passphrase);
|
||||
if (!result.decrypt(passphrase)) {
|
||||
throw new IllegalStateException("Could not decrypt private key of "+this+"; probably incorrect passphrase?");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user