Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve upgrade workflow for embedded Tomcat #200

Merged
merged 18 commits into from
Mar 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
.gradle
build/
out/
lib/

.idea/dataSources.local.xml
# Unless specified, ignore all IDEA project files
.idea/
!.idea/codeStyles/codeStyleConfig.xml
!.idea/inspectionProfiles/Project_Default.xml
!.idea/runConfigurations/Debug_plugins.xml
!.idea/compiler.xml
!.idea/misc.xml
!.idea/vcs.xml

# Ignore zip files created by build
src/main/resources/moduleTemplate.zip
Expand Down
8 changes: 0 additions & 8 deletions .idea/.gitignore

This file was deleted.

7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ on how to do that, including how to develop and test locally and the versioning

_Note: 1.28.0 and later require Gradle 7_

### 2.6.0
*Released*: 11 March 2024
(Earliest compatible LabKey version: 24.2)
* Include `application.properties` in embedded distributions
* Remove version from executable server jar name: `labkeyServer.jar`
* Make `startTomcat` task work for embedded Tomcat on Windows

### 2.5.1
*Released*: 11 March 2024
(Earliest compatible LabKey version: 24.2)
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ dependencies {
}

group 'org.labkey.build'
project.version = "2.6.0-SNAPSHOT"
project.version = "2.7.0-SNAPSHOT"

gradlePlugin {
plugins {
Expand Down
163 changes: 163 additions & 0 deletions distributionResources/embedded/config/application.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
server.port=8080

## To use ssl, update the properties below for your local installation

#server.ssl.enabled=true
#server.ssl.enabled-protocols=TLSv1.3,TLSv1.2,TLSv1.1
#server.ssl.protocol=TLS
#server.ssl.key-alias=tomcat
#server.ssl.key-store=@@keyStore@@
#server.ssl.key-store-password=@@keyStorePassword@@
## Typically either PKCS12 or JKS
#server.ssl.key-store-type=PKCS12
#server.ssl.ciphers=HIGH:!ADH:!EXP:!SSLv2:!SSLv3:!MEDIUM:!LOW:!NULL:!aNULL

## HTTP-only port for servers that need to handle both HTTPS (configure via server.port and server.ssl above) and HTTP
#context.httpPort=8080

## Database connections. All deployments need a labkeyDataSource as their primary database. Add additional external
## data sources by specifying the required properties (at least driverClassName, url, username, and password)
## with a prefix of context.resources.jdbc.<dataSourceName>.
context.resources.jdbc.labkeyDataSource.type=javax.sql.DataSource
context.resources.jdbc.labkeyDataSource.driverClassName=org.postgresql.Driver
context.resources.jdbc.labkeyDataSource.url=jdbc:postgresql://localhost/labkey
context.resources.jdbc.labkeyDataSource.username=@@jdbcUser@@
context.resources.jdbc.labkeyDataSource.password=@@jdbcPassword@@
context.resources.jdbc.labkeyDataSource.maxTotal=50
context.resources.jdbc.labkeyDataSource.maxIdle=10
context.resources.jdbc.labkeyDataSource.maxWaitMillis=120000
context.resources.jdbc.labkeyDataSource.accessToUnderlyingConnectionAllowed=true
context.resources.jdbc.labkeyDataSource.validationQuery=SELECT 1
#context.resources.jdbc.labkeyDataSource.logQueries=true
#context.resources.jdbc.labkeyDataSource.displayName=Alternate Display Name

#context.resources.jdbc.@@extraJdbcDataSource@@.driverClassName=@@extraJdbcDriverClassName@@
#context.resources.jdbc.@@extraJdbcDataSource@@.url=@@extraJdbcUrl@@
#context.resources.jdbc.@@extraJdbcDataSource@@.username=@@extraJdbcUsername@@
#context.resources.jdbc.@@extraJdbcDataSource@@.password=@@extraJdbcPassword@@

context.encryptionKey=@@encryptionKey@@

## By default, we deploy to the root context path. However, some servers have historically used /labkey or even /cpas
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is to be included with a distribution, I think this comment should be updated to instruct users on intended usage, not historical context.

#context.contextPath=/labkey

## Using a legacy context path provides backwards compatibility with old deployments. A typical use case would be to
## deploy to the root context (the default) and configure /labkey as the legacy path. GETs will be redirected.
## All other methods (POSTs, PUTs, etc) will be handled server-side via a servlet forward.
#context.legacyContextPath=/labkey

## Other webapps to be deployed, most commonly to deliver a set of static files. The context path to deploy into is the
## property name after the "context.additionalWebapps." prefix, and the value is the location of the webapp on disk
#context.additionalWebapps.firstContextPath=/my/webapp/path
#context.additionalWebapps.secondContextPath=/my/other/webapp/path

#context.oldEncryptionKey=
#context.requiredModules=
#context.pipelineConfig=/path/to/pipeline/config/dir
#context.serverGUID=
#context.bypass2FA=true
#context.workDirLocation=/path/to/desired/workDir

mail.smtpHost=@@smtpHost@@
mail.smtpPort=@@smtpPort@@
mail.smtpUser=@@smtpUser@@
#mail.smtpFrom=@@smtpFrom@@
#mail.smtpPassword=@@smtpPassword@@
#mail.startTlsEnable=@@smtpStartTlsEnable@@
#mail.smtpSocketFactoryClass=@@smtpSocketFactoryClass@@
#mail.smtpAuth=@@smtpAuth@@

## Optional - JMS configuration for remote ActiveMQ message management for distributed pipeline jobs
## https://www.labkey.org/Documentation/wiki-page.view?name=jmsQueue
#context.resources.jms.ConnectionFactory.type=org.apache.activemq.ActiveMQConnectionFactory
#context.resources.jms.ConnectionFactory.factory=org.apache.activemq.jndi.JNDIReferenceFactory
#context.resources.jms.ConnectionFactory.description=JMS Connection Factory
## Use an in-process ActiveMQ queue
#context.resources.jms.ConnectionFactory.brokerURL=vm://localhost?broker.persistent=false&broker.useJmx=false
## Use an out-of-process ActiveMQ queue
#context.resources.jms.ConnectionFactory.brokerURL=tcp://localhost:61616
#context.resources.jms.ConnectionFactory.brokerName=LocalActiveMQBroker

## Optional - LDAP configuration for LDAP group/user synchronization
## https://www.labkey.org/Documentation/wiki-page.view?name=LDAP_sync
#context.resources.ldap.ConfigFactory.type=org.labkey.premium.ldap.LdapConnectionConfigFactory
#context.resources.ldap.ConfigFactory.factory=org.labkey.premium.ldap.LdapConnectionConfigFactory
#context.resources.ldap.ConfigFactory.host=myldap.mydomain.com
#context.resources.ldap.ConfigFactory.port=389
#context.resources.ldap.ConfigFactory.principal=cn=read_user
#context.resources.ldap.ConfigFactory.credentials=read_user_password
#context.resources.ldap.ConfigFactory.useTls=false
#context.resources.ldap.ConfigFactory.useSsl=false
#context.resources.ldap.ConfigFactory.sslProtocol=SSLv3

## HTTP session timeout for users - defaults to 30 minutes
#server.servlet.session.timeout=30m

## Enable shutdown endpoint. Allows server to be shutdown with a POST to 'localhost:8081/actuator/shutdown
#management.endpoint.shutdown.enabled=true
#management.endpoints.enabled-by-default=false
#management.endpoints.web.exposure.include=*
#management.server.port=8081

## Don't show the Spring banner on startup
spring.main.banner-mode=off
#logging.config=path/to/alternative/log4j2.xml

## Optional - JMS configuration for remote ActiveMQ message management for distributed pipeline jobs
## https://www.labkey.org/Documentation/wiki-page.view?name=jmsQueue
#context.resources.jms.name=jms/ConnectionFactory
#context.resources.jms.type=org.apache.activemq.ActiveMQConnectionFactory
#context.resources.jms.factory=org.apache.activemq.jndi.JNDIReferenceFactory
#context.resources.jms.description=JMS Connection Factory
#context.resources.jms.brokerURL=vm://localhost?broker.persistent=false&broker.useJmx=false
#context.resources.jms.brokerName=LocalActiveMQBroker

## Turn on JSON-formatted HTTP access logging to stdout. See issue 48565
## https://tomcat.apache.org/tomcat-9.0-doc/config/valve.html#JSON_Access_Log_Valve
#jsonaccesslog.enabled=true

## Optional configuration, modeled on the non-JSON Spring Boot properties
## https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html#application-properties.server.server.tomcat.accesslog.buffered
#jsonaccesslog.pattern=%h %t %m %U %s %b %D %S "%{Referer}i" "%{User-Agent}i" %{LABKEY.username}s
#jsonaccesslog.condition-if=attributeName
#jsonaccesslog.condition-unless=attributeName

## Define one or both of 'csp.report' and 'csp.enforce' to enable Content Security Policy (CSP) headers
## Do not use these examples for any production environment without understanding the meaning of each directive!

## example usage 1 - very strict, disallows 'external' websites, disallows unsafe-inline, but only reports violations (does not enforce)

#csp.report=\
# default-src 'self';\
# connect-src 'self' ${LABKEY.ALLOWED.CONNECTIONS} ;\
# object-src 'none' ;\
# style-src 'self' 'unsafe-inline' ;\
# img-src 'self' data: ;\
# font-src 'self' data: ;\
# script-src 'unsafe-eval' 'strict-dynamic' 'nonce-${REQUEST.SCRIPT.NONCE}';\
# base-uri 'self' ;\
# upgrade-insecure-requests ;\
# frame-ancestors 'self' ;\
# report-uri https://www.labkey.org/admin-contentsecuritypolicyreport.api?${CSP.REPORT.PARAMS} ;

## example usage 2 - less strict but enforces directives, (NOTE: unsafe-inline is still required for many modules)

#csp.enforce=\
# default-src 'self' https: ;\
# connect-src 'self' https: ${LABKEY.ALLOWED.CONNECTIONS};\
# object-src 'none' ;\
# style-src 'self' https: 'unsafe-inline' ;\
# img-src 'self' data: ;\
# font-src 'self' data: ;\
# script-src 'unsafe-inline' 'unsafe-eval' 'strict-dynamic' 'nonce-${REQUEST.SCRIPT.NONCE}';\
# base-uri 'self' ;\
# upgrade-insecure-requests ;\
# frame-ancestors 'self' ;\
# report-uri https://www.labkey.org/admin-contentsecuritypolicyreport.api?${CSP.REPORT.PARAMS} ;


## Enable tomcat access log
#server.tomcat.basedir=.
#server.tomcat.accesslog.enabled=true
#server.tomcat.accesslog.directory=logs
#server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b %D %S %I "%{Referrer}i" "%{User-Agent}i" %{LABKEY.username}s
2 changes: 0 additions & 2 deletions distributionResources/embedded/manual-upgrade.sh

This file was deleted.

32 changes: 20 additions & 12 deletions src/main/groovy/org/labkey/gradle/task/ModuleDistribution.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
package org.labkey.gradle.task

import org.apache.commons.lang3.StringUtils

import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.Project
Expand Down Expand Up @@ -237,7 +236,7 @@ class ModuleDistribution extends DefaultTask

private String getEmbeddedTomcatJarPath()
{
return BuildUtils.getBuildDirFile(project, "labkeyServer-${project.version}.jar").path
return BuildUtils.getBuildDirFile(project, "labkeyServer.jar").path
}

private String getTarArchivePath()
Expand Down Expand Up @@ -422,6 +421,10 @@ class ModuleDistribution extends DefaultTask
zipfileset(dir: "${BuildUtils.getBuildDirPath(project)}/") {
include(name: "labkeywebapp/**")
}
zipfileset(dir: "${BuildUtils.getBuildDirPath(project)}/",
prefix: "${DistributionExtension.DIST_FILE_DIR}") {
include(name: "VERSION")
}
}

project.copy {
Expand Down Expand Up @@ -463,10 +466,7 @@ class ModuleDistribution extends DefaultTask
include(name: "VERSION")
}

tarfileset(dir: BuildUtils.getBuildDirFile(project, "embedded"), prefix: archiveName) {
// include(name: "manual-upgrade.sh")
include(name: "README.txt")
}
tarfileset(dir: "${BuildUtils.getBuildDirPath(project)}/embedded", prefix: archiveName)
}
}

Expand All @@ -491,11 +491,7 @@ class ModuleDistribution extends DefaultTask
include(name: "VERSION")
}

zipfileset(dir: "${BuildUtils.getBuildDirPath(project)}/embedded/",
prefix: "${archiveName}") {
// include(name: "manual-upgrade.sh")
include(name: "README.txt")
}
zipfileset(dir: "${BuildUtils.getBuildDirPath(project)}/embedded/", prefix: "${archiveName}")
}
}

Expand All @@ -510,6 +506,16 @@ class ModuleDistribution extends DefaultTask
copy.into(project.layout.buildDirectory)
copy.setDuplicatesStrategy(DuplicatesStrategy.INCLUDE)
})
// Prefer files from 'server/configs/webapps' if they exist
File serverConfigDir = project.rootProject.file("server/configs/webapps/")
if (serverConfigDir.exists()) {
project.copy({ CopySpec copy ->
copy.from(serverConfigDir)
copy.exclude "*.xml"
copy.into(project.layout.buildDirectory)
copy.setDuplicatesStrategy(DuplicatesStrategy.INCLUDE)
})
}
// Allow distributions to include custom README
File resources = project.file("resources")
if (resources.isDirectory()) {
Expand Down Expand Up @@ -562,6 +568,8 @@ class ModuleDistribution extends DefaultTask

private void writeVersionFile()
{
Files.write(getVersionFile().toPath(), ((String) project.version).getBytes())
// Include TeamCity buildUrl, if present.
def buildUrl = StringUtils.trimToEmpty(System.getenv("BUILD_URL"))
Files.write(getVersionFile().toPath(), "${project.version}\n${buildUrl}".trim().getBytes())
}
}
3 changes: 2 additions & 1 deletion src/main/groovy/org/labkey/gradle/task/StartTomcat.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ class StartTomcat extends DefaultTask
String javaHome = TeamCityExtension.getTeamCityProperty(project, "tomcatJavaHome", System.getenv("JAVA_HOME"))
if (StringUtils.isEmpty(javaHome))
throw new GradleException("JAVA_HOME must be set in order to start your embedded tomcat server.")
File javaExec = new File(javaHome, "bin/java")
File javaBin = new File(javaHome, "bin")
File javaExec = new File(javaBin, SystemUtils.IS_OS_WINDOWS ? "java.exe" : "java")
if (!javaExec.exists())
throw new GradleException("Invalid value for JAVA_HOME. Could not find java command in ${javaExec}")
String[] commandParts = [javaExec.getAbsolutePath()]
Expand Down