Skip to content

Commit

Permalink
Merge pull request #22 from eclipse-ee4j/ondromih-change-passwords
Browse files Browse the repository at this point in the history
Custom init.sh script, master/admin passwords via ENV vars
  • Loading branch information
dmatej authored Jan 21, 2025
2 parents 062a858 + 8812308 commit 2a6bd31
Show file tree
Hide file tree
Showing 12 changed files with 488 additions and 71 deletions.
9 changes: 5 additions & 4 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>org.glassfish.main.distributions</groupId>
Expand Down Expand Up @@ -30,7 +30,7 @@
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.11.3</version>
<version>5.11.4</version>
<scope>test</scope>
</dependency>
<dependency>
Expand All @@ -42,13 +42,14 @@
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>1.20.3</version>
<version>1.20.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.20.3</version>
<version>1.20.4</version>
<scope>test</scope>
</dependency>
</dependencies>

Expand Down
1 change: 1 addition & 0 deletions src/main/resources/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ RUN true \
&& echo "Generating password file at ${AS_PASSWORD_FILE} ..." \
&& set +x \
&& echo "AS_ADMIN_PASSWORD=${AS_ADMIN_PASSWORD}" > "${AS_PASSWORD_FILE}" \
&& chown glassfish:glassfish "${AS_PASSWORD_FILE}" \
&& echo "AS_ADMIN_PASSWORD=" > "${PATH_GF_PASSWORD_FILE_FOR_CHANGE}" \
&& echo "AS_ADMIN_NEWPASSWORD=${AS_ADMIN_PASSWORD}" >> "${PATH_GF_PASSWORD_FILE_FOR_CHANGE}" \
&& echo "" >> "${PATH_GF_PASSWORD_FILE_FOR_CHANGE}" \
Expand Down
35 changes: 35 additions & 0 deletions src/main/resources/docker-entrypoint.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,41 @@
#!/bin/bash
set -e;

change_passwords () {
local PWD_FILE=/tmp/passwordfile
local COMMAND=
rm -rf $PWD_FILE

if [ x"${AS_ADMIN_PASSWORD}" != x ]; then
echo -e "AS_ADMIN_PASSWORD=admin\nAS_ADMIN_NEWPASSWORD=${AS_ADMIN_PASSWORD}" >> $PWD_FILE
COMMAND="change-admin-password --passwordfile=${PWD_FILE}"
echo "AS_ADMIN_PASSWORD=${AS_ADMIN_PASSWORD}" > "${AS_PASSWORD_FILE}"
fi

if [ x"${AS_ADMIN_MASTERPASSWORD}" != x ]; then
echo -e "AS_ADMIN_MASTERPASSWORD=changeit\nAS_ADMIN_NEWMASTERPASSWORD=${AS_ADMIN_MASTERPASSWORD}" >> ${PWD_FILE}
COMMAND="${COMMAND}
change-master-password --passwordfile=${PWD_FILE} --savemasterpassword=true"
fi

if [ x"${COMMAND}" != x ]; then
printf "${COMMAND}" | asadmin --interactive=false
fi

rm -rf ${PWD_FILE}
}

change_passwords

if [ -f custom/init.sh ]; then
/bin/bash custom/init.sh
fi

if [ -f custom/init.asadmin ]; then
asadmin --interactive=false multimode -f custom/init.asadmin
fi


if [ "$1" != 'asadmin' -a "$1" != 'startserv' -a "$1" != 'runembedded' ]; then
exec "$@"
fi
Expand Down
111 changes: 110 additions & 1 deletion src/main/resources/docs/content.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ To deploy an application, copy the application into the Docker image or mount th
docker run -p 8080:8080 @docker.glassfish.repository@ runembedded myapp.war
```


## Run an application with GlassFish in Docker

You can run an application located in your filesystem with GlassFIsh in a Docker container.
Expand Down Expand Up @@ -100,6 +99,116 @@ If you need suspend GlassFish startup until you connect the debugger, use the `-
docker run -p 9009:9009 -p 8080:8080 -p 4848:4848 @docker.glassfish.repository@ startserv --suspend
```

## Environment variables

The following environment variables can be set to configure GlassFish Server:

* `AS_ADMIN_MASTERPASSWORD` - to change the default master password
* `AS_ADMIN_PASSWORD` - to change the default admin password

The following environment variables are read-only and can be used in derived Docker images or scripts:

* `AS_PASSWORD_FILE` - path to the password file with the admin password. It's applied by default to any asadmin command
* `AS_ADMIN_USER` - name of the admin user. It's applied by default to any asadmin command
* `PATH_GF_HOME` - directory of the GlassFish installation. Also the default working directory
* `PATH_GF_SERVER_LOG` - path to the server log file

## Additional configuration

It's possible to specify custom commands to run in the Docker container before GlassFish server starts. The following methods are supported:

* `${PATH_GF_HOME}/custom/init.sh` - Execute any `bash` script, which can execute admin commands via the `asadmin` command line tool
* `${PATH_GF_HOME}/custom/init.asadmin` - Execute asadmin commands directly

If both of the above scripts are present, they are executed in this order:

1. `init.sh`
2. `init.asadmin`

However, always consider to executing any asadmin configuration commands during build, because configuring the server at container startup will prolong the startup time.

### Execute asadmin commands before server startup

Just create a file `${PATH_GF_HOME}/custom/init.asadmin` (`/opt/glassfish7/custom/init.asadmin`), the commands will be executed before GlassFish server starts.

Within the `init.asadmin` file, you can specify any asadmin command. Most of the commands require that the server is running, so you'll need to start the server first, run the configuration commands, and then stop the server.

For example, to start GlassFish, increase the maximum amount of threads, and then stop it, the `init.asadmin` file can contain:

```
start-domain
set configs.config.server-config.thread-pools.thread-pool.http-thread-pool.max-thread-pool-size=1000
stop-domain
```

You can provide the file by mounting its directory to the `/opt/glassfish7/custom` directory in the container when running the container:

```
docker run -v ./custom:/opt/glassfish7/custom -p 8080:8080 -ti @docker.glassfish.repository@
```

### Execute a `bash` script before server startup

Just create a Bash script `${PATH_GF_HOME}/custom/init.sh` (`/opt/glassfish7/custom/init.sh`), it will be executed before GlassFish server starts.

Within the `init.sh` script, you can run any asadmin command, with `asadmin --interactive=false multimode COMMAND`. Most of the commands require that the server is running, so you'll need to start the server first, run the configuration commands, and then stop the server. If you need to run multiple commands, we recomment running asadmin commands in a single "multimode" asadmin execution to run them faster, with commands provided either on standard input or in a separate file via the `asadmin --interactive=false multimode -f` option.

----

**NOTE:** If you only need to execute `asadmin` commands before server startup, it's easier to use the init.asadmin script to execute them directly, without a `bash` script.

----

For example, to start GlassFish, increase the maximum amount of threads, and then stop it, the `init.sh` script can contain:

```
echo "start-domain
set configs.config.server-config.thread-pools.thread-pool.http-thread-pool.max-thread-pool-size=1000
stop-domain" | asadmin --interactive=false
```

You can provide the script by mounting its directory to the `/opt/glassfish7/custom` directory in the container when running the container:

```
docker run -v ./custom:/opt/glassfish7/custom -p 8080:8080 -ti @docker.glassfish.repository@
```

### Execute `asadmin` commands during Docker image build

Applying the configuration can be a lengthy operation. If you can configure the server during build time, it's recommended running asadmin configuration commands in a custom Docker image. This moves the configuration step to the image build time instead of runtime.

To do it, simply add `RUN instructions that run `asadmin` script with the usual arguments. For example, to move the example configuration from the `init.sh` script above to Dockerfile:

File `Dockerfile`:

```
FROM @docker.glassfish.repository@
RUN printf "start-domain \n \
set configs.config.server-config.thread-pools.thread-pool.http-thread-pool.max-thread-pool-size=1000 \n \
stop-domain" | asadmin --interactive=false
```

Or you can put the asadmin commands into a separate file and run it using `asadmin --interactive=false multimode -f`. For example:

File `commands.asadmin`:

```
start-domain
set configs.config.server-config.thread-pools.thread-pool.http-thread-pool.max-thread-pool-size=1000
stop-domain
```

File `Dockerfile`:

```
FROM @docker.glassfish.repository@
COPY commands.asadmin commands.asadmin
RUN asadmin --interactive=false multimode -f commands.asadmin
```

## Examples of advanced usage

Let's try something more complicated.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2024 Contributors to the Eclipse Foundation.
* Copyright (c) 2024,2025 Contributors to the Eclipse Foundation.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
Expand All @@ -13,14 +13,9 @@
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/

package org.glassfish.main.distributions.docker;

import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.net.http.HttpResponse;

import org.junit.jupiter.api.Test;
import org.testcontainers.containers.GenericContainer;
Expand All @@ -30,6 +25,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.stringContainsInOrder;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.glassfish.main.distributions.docker.HttpUtilities.getServerDefaultRoot;

/**
*
Expand All @@ -40,23 +36,13 @@ public class AsadminIT {
@SuppressWarnings({"rawtypes", "resource"})
@Container
private final GenericContainer server = new GenericContainer<>(System.getProperty("docker.glassfish.image"))
.withCommand("asadmin start-domain").withExposedPorts(8080)
.withLogConsumer(o -> System.err.print("GF: " + o.getUtf8String()));
.withCommand("asadmin start-domain").withExposedPorts(8080)
.withLogConsumer(o -> System.err.print("GF: " + o.getUtf8String()));

@Test
void getRoot() throws Exception {
URL url = URI.create("http://localhost:" + server.getMappedPort(8080) + "/").toURL();
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
String content;
try {
connection.setRequestMethod("GET");
assertEquals(200, connection.getResponseCode(), "Response code");
try (InputStream in = connection.getInputStream()) {
content = new String(in.readAllBytes(), StandardCharsets.UTF_8);
}
} finally {
connection.disconnect();
}
assertThat(content, stringContainsInOrder("Eclipse GlassFish", "index.html", "production-quality"));
void rootResourceGivesOkWithDefaultResponse() throws Exception {
final HttpResponse<String> defaultRootResponse = getServerDefaultRoot(server);
assertEquals(200, defaultRootResponse.statusCode(), "Response status code");
assertThat(defaultRootResponse.body(), stringContainsInOrder("Eclipse GlassFish", "index.html", "production-quality"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright (c) 2025 Contributors to the Eclipse Foundation
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package org.glassfish.main.distributions.docker;

import java.net.http.HttpResponse;

import org.junit.jupiter.api.Test;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

import static org.glassfish.main.distributions.docker.HttpUtilities.getServerDefaultRoot;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.stringContainsInOrder;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.glassfish.main.distributions.docker.HttpUtilities.getAdminResource;

/**
*
* @author Ondro Mihalyi
*/
@Testcontainers
public class ChangePasswordsIT {

@SuppressWarnings({"rawtypes", "resource"})
@Container
private final GenericContainer server = new GenericContainer<>(System.getProperty("docker.glassfish.image"))
.withExposedPorts(8080, 4848)
.withEnv("AS_ADMIN_MASTERPASSWORD", "mymasterpassword")
.withEnv("AS_ADMIN_PASSWORD", "myadminpassword")
.withLogConsumer(o -> System.err.print("GF: " + o.getUtf8String()));

@Test
void rootResourceGivesOkWithDefaultResponse() throws Exception {
final HttpResponse<String> defaultRootResponse = getServerDefaultRoot(server);
assertEquals(200, defaultRootResponse.statusCode(), "Response status code");
assertThat(defaultRootResponse.body(), stringContainsInOrder("Eclipse GlassFish", "index.html", "production-quality"));
}

@Test
void customAdminPassword() throws Exception {
final HttpResponse<String> adminResourceResponse = getAdminResource(server, "/management/domain.json",
new UserPassword("admin", "myadminpassword"));
assertEquals(200, adminResourceResponse.statusCode(), "Response status code");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright (c) 2025 Contributors to the Eclipse Foundation.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/

package org.glassfish.main.distributions.docker;

import java.net.http.HttpResponse;

import org.junit.jupiter.api.Test;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

import static org.glassfish.main.distributions.docker.HttpUtilities.getServerDefaultRoot;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.stringContainsInOrder;
import static org.junit.jupiter.api.Assertions.assertEquals;

@Testcontainers
public class DefaultIT {

@SuppressWarnings({"rawtypes", "resource"})
@Container
private final GenericContainer server = new GenericContainer<>(System.getProperty("docker.glassfish.image"))
.withExposedPorts(8080)
.withLogConsumer(o -> System.err.print("GF: " + o.getUtf8String()));

@Test
void rootResourceGivesOkWithDefaultResponse() throws Exception {
final HttpResponse<String> defaultRootResponse = getServerDefaultRoot(server);
assertEquals(200, defaultRootResponse.statusCode(), "Response status code");
assertThat(defaultRootResponse.body(), stringContainsInOrder("Eclipse GlassFish", "index.html", "production-quality"));
}
}
Loading

0 comments on commit 2a6bd31

Please sign in to comment.