├── .dockerignore
├── .github
├── actions
│ └── publish_docker_image
│ │ └── action.yml
├── dependabot.yml
└── workflows
│ ├── build_docker_image.yml
│ └── erddap_version.yml
├── .gitignore
├── Dockerfile
├── README.md
├── datasets.d.sh
├── entrypoint.sh
├── examples
├── data
│ └── trees.csv
├── datasets.d
│ └── trees.csv.xml
├── docker-compose.yml
└── rsyslog
│ ├── rsyslog.conf
│ └── rsyslog.d
│ ├── 00-imports.conf
│ ├── 10-tomcat.conf
│ └── 20-erddap.conf
├── files
└── setenv.sh
├── init.d
└── 10-remove-hdf-nc-range-request-exclusion.sh
├── make-dataset-xml.sh
├── test
└── datasets.d-inactive-files.sh
└── update-server-xml.sh
/.dockerignore:
--------------------------------------------------------------------------------
1 | .git
2 | .gitignore
3 | README.md
4 | logs
5 | examples
6 |
--------------------------------------------------------------------------------
/.github/actions/publish_docker_image/action.yml:
--------------------------------------------------------------------------------
1 | name: Publish Docker images
2 | description: Publish Docker images to Docker Hub
3 | inputs:
4 | DOCKER_USERNAME:
5 | description: Docker Hub username
6 | required: true
7 | DOCKER_TOKEN:
8 | description: Docker Hub token
9 | required: true
10 | runs:
11 | using: composite
12 | steps:
13 | - name: Docker meta
14 | id: meta
15 | uses: docker/metadata-action@v5
16 | with:
17 | images: |
18 | axiom/docker-erddap
19 | flavor: |
20 | suffix=-jdk21-openjdk,onlatest=true
21 | tags: |
22 | type=raw,value=latest,enable={{is_default_branch}}
23 | type=ref,event=tag
24 |
25 | - name: Login to Docker Hub
26 | uses: docker/login-action@v3
27 | with:
28 | username: ${{ inputs.DOCKER_USERNAME }}
29 | password: ${{ inputs.DOCKER_TOKEN }}
30 |
31 | - name: Push to Docker Hub
32 | uses: docker/build-push-action@v6
33 | with:
34 | push: true
35 | platforms: linux/amd64,linux/arm64/v8
36 | tags: ${{ steps.meta.outputs.tags }}
37 | labels: ${{ steps.meta.outputs.labels }}
38 | cache-from: type=gha
39 | cache-to: type=gha,mode=max
40 |
41 | - name: Update repo description
42 | uses: peter-evans/dockerhub-description@v4
43 | with:
44 | username: ${{ inputs.DOCKER_USERNAME }}
45 | password: ${{ inputs.DOCKER_TOKEN }}
46 | repository: axiom/docker-erddap
47 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: docker
4 | directory: "/"
5 | schedule:
6 | interval: weekly
7 |
--------------------------------------------------------------------------------
/.github/workflows/build_docker_image.yml:
--------------------------------------------------------------------------------
1 | name: Build Docker images
2 |
3 | on:
4 | workflow_dispatch:
5 | pull_request:
6 | push:
7 | branches:
8 | - main
9 | release:
10 | types:
11 | - published
12 |
13 | jobs:
14 | build:
15 | name: Build and test Docker Image
16 | runs-on: ubuntu-22.04
17 | timeout-minutes: 10
18 |
19 | steps:
20 | - name: Set up Docker Buildx
21 | uses: docker/setup-buildx-action@v3
22 |
23 | - name: Build image for testing
24 | uses: docker/build-push-action@v6
25 | with:
26 | push: false
27 | tags: docker-erddap-test
28 | cache-from: type=gha
29 | cache-to: type=gha,mode=max
30 | outputs: type=docker
31 |
32 | - name: Run Docker Image in Background
33 | run: docker run -d -p 8080:8080 docker-erddap-test
34 |
35 | - name: Check that ERDDAP Docker Image will return a 200
36 | uses: ifaxity/wait-on-action@v1
37 | timeout-minutes: 1
38 | with:
39 | resource: http://localhost:8080/erddap/index.html
40 |
41 | - name: Checkout custom actions
42 | uses: actions/checkout@v4
43 | with:
44 | sparse-checkout: |
45 | .github
46 |
47 | - name: Publish Docker images
48 | if: |
49 | ((github.event_name == 'release' && github.event.action == 'published') || github.ref == 'refs/heads/main')
50 | && github.repository == 'axiom-data-science/docker-erddap'
51 | uses: ./.github/actions/publish_docker_image
52 | with:
53 | DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
54 | DOCKER_TOKEN: ${{ secrets.DOCKER_PASSWORD }}
55 |
--------------------------------------------------------------------------------
/.github/workflows/erddap_version.yml:
--------------------------------------------------------------------------------
1 | name: Check ERDDAP Version
2 |
3 | on:
4 | schedule:
5 | - cron: "0 13 * * 1-5" # Check at 8 am on weekdays
6 | workflow_dispatch:
7 | # Once merged, under actions > Check ERDDAP Version there should be a 'Run Workflow' button which manually trigger a run
8 |
9 |
10 |
11 | jobs:
12 | version:
13 | name: Check the current ERDDAP version
14 | runs-on: ubuntu-22.04
15 | timeout-minutes: 5
16 | if: github.repository == 'axiom-data-science/docker-erddap'
17 |
18 | steps:
19 | - name: Checkout
20 | uses: actions/checkout@v4
21 |
22 | - name: Get ERDDAP versions, and create an issue if it is out of date
23 | uses: actions/github-script@v3
24 | with:
25 | script: |
26 | const repo_name = "docker-erddap"
27 | const repo_owner = "axiom-data-science"
28 | const assign_user = "srstsavage"
29 |
30 | const fs = require("fs")
31 |
32 | const release = await github.repos.getLatestRelease({
33 | owner: "ERDDAP",
34 | repo: "erddap"
35 | })
36 | const tag = release.data.tag_name
37 |
38 | const dockerfile = fs.readFileSync(`${process.env.GITHUB_WORKSPACE}/Dockerfile`, "utf-8")
39 | const lines = dockerfile.split("\n")
40 | const docker_version_line = lines.find(line => line.includes("ERDDAP_VERSION"))
41 | const docker_version = `v${docker_version_line.split("=")[1]}`
42 |
43 | if (tag === docker_version) {
44 | console.log(`Latest Docker version (${docker_version}) matches the latest available ERDDAP/erddap release`)
45 |
46 | return
47 | }
48 |
49 | console.log(`Latest Docker version (${docker_version}) does not match the latest available ERDDAP/erddap release (${tag})`)
50 |
51 | const query = `query($owner: String!, $name: String!) {
52 | repository(owner: $owner, name: $name) {
53 | issues(first: 5, states: OPEN) {
54 | nodes {
55 | id
56 | title
57 | }
58 | }
59 | }
60 | }`
61 |
62 | const issue_label = "erddapversion"
63 | const issue_title = `Update ERDDAP to ${tag}`
64 |
65 | const variables = {
66 | owner: repo_owner,
67 | name: repo_name,
68 | label: issue_label
69 | }
70 | const result = await github.graphql(query, variables)
71 | const issues = result.repository.issues.nodes
72 | const issue_for_version = issues.find(issue => issue.title.includes(issue_title))
73 |
74 | if (issue_for_version !== undefined) {
75 | console.log(`There is already an issue created for the latest version of ERDDAP`)
76 | return
77 | }
78 |
79 | console.log("Creating a new issue to update the ERDDAP version")
80 |
81 | const issue_body = `
82 | ERDDAP has been updated to ${tag}!
83 |
84 | @${assign_user} Please update and test the version in the Dockerfile.
85 | `
86 |
87 | const issue = await github.issues.create({
88 | owner: repo_owner,
89 | repo: repo_name,
90 | title: issue_title,
91 | labels: [issue_label],
92 | body: issue_body,
93 | assignees: [
94 | assign_user
95 | ]
96 | })
97 |
98 | console.log(`New issue created at ${issue.data.html_url}`)
99 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | logs
2 | examples/erddapData
3 | examples/tomcatLogs
4 | examples/.env
5 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | ARG BASE_IMAGE=tomcat:10.1.26-jdk21-temurin-jammy
2 | #referencing a specific image digest pins our unidata tomcat-docker image to platform amd64 (good)
3 | ARG UNIDATA_TOMCAT_IMAGE=unidata/tomcat-docker:10-jdk17@sha256:af7d3fecec753cbd438f25881deeaf48b40ac1f105971d6f300252e104e39fb2
4 | FROM ${UNIDATA_TOMCAT_IMAGE} AS unidata-tomcat-image
5 | FROM ${BASE_IMAGE}
6 |
7 | #use approaches and hardened files from https://github.com/Unidata/tomcat-docker
8 | #note: we don't inherit directly from Unidata/tomcat-docker to allow more
9 | #flexibility in building images using different tomcat base images, architectures, etc
10 | RUN apt-get update && \
11 | apt-get install -y --no-install-recommends \
12 | gosu \
13 | zip \
14 | unzip \
15 | && \
16 | # Cleanup
17 | apt-get clean && \
18 | rm -rf /var/lib/apt/lists/* && \
19 | # Eliminate default web applications
20 | rm -rf ${CATALINA_HOME}/webapps/* && \
21 | rm -rf ${CATALINA_HOME}/webapps.dist && \
22 | # Obscuring server info
23 | cd ${CATALINA_HOME}/lib && \
24 | mkdir -p org/apache/catalina/util/ && \
25 | unzip -j catalina.jar org/apache/catalina/util/ServerInfo.properties \
26 | -d org/apache/catalina/util/ && \
27 | sed -i 's/server.info=.*/server.info=Apache Tomcat/g' \
28 | org/apache/catalina/util/ServerInfo.properties && \
29 | zip -ur catalina.jar \
30 | org/apache/catalina/util/ServerInfo.properties && \
31 | rm -rf org && cd ${CATALINA_HOME} && \
32 | # Setting restrictive umask container-wide
33 | echo "session optional pam_umask.so" >> /etc/pam.d/common-session && \
34 | sed -i 's/UMASK.*022/UMASK 007/g' /etc/login.defs
35 |
36 | # Security enhanced web.xml
37 | COPY --from=unidata-tomcat-image ${CATALINA_HOME}/conf/web.xml ${CATALINA_HOME}/conf/
38 |
39 | # Security enhanced server.xml
40 | COPY --from=unidata-tomcat-image ${CATALINA_HOME}/conf/server.xml ${CATALINA_HOME}/conf/
41 |
42 | ARG ERDDAP_VERSION=2.25.1
43 | ARG ERDDAP_CONTENT_VERSION=1.0.0
44 | ARG ERDDAP_WAR_URL="https://github.com/ERDDAP/erddap/releases/download/v${ERDDAP_VERSION}/erddap.war"
45 | ARG ERDDAP_CONTENT_URL="https://github.com/ERDDAP/erddapContent/archive/refs/tags/content${ERDDAP_CONTENT_VERSION}.zip"
46 | ENV ERDDAP_bigParentDirectory=/erddapData
47 |
48 | RUN apt-get update && apt-get install -y unzip xmlstarlet \
49 | && if ! command -v gosu &> /dev/null; then apt-get install -y gosu; fi \
50 | && rm -rf /var/lib/apt/lists/*
51 |
52 | ARG BUST_CACHE=1
53 | RUN \
54 | mkdir -p /tmp/dl && \
55 | curl -fSL "${ERDDAP_WAR_URL}" -o /tmp/dl/erddap.war && \
56 | unzip /tmp/dl/erddap.war -d ${CATALINA_HOME}/webapps/erddap/ && \
57 | curl -fSL "${ERDDAP_CONTENT_URL}" -o /tmp/dl/erddapContent.zip && \
58 | unzip /tmp/dl/erddapContent.zip -d /tmp/dl/erddapContent && \
59 | find /tmp/dl/erddapContent -type d -name content -exec cp -r "{}" ${CATALINA_HOME} \; && \
60 | rm -rf /tmp/dl && \
61 | sed -i 's##\n' ${CATALINA_HOME}/conf/context.xml && \
62 | rm -rf /tmp/* /var/tmp/* && \
63 | mkdir -p ${ERDDAP_bigParentDirectory}
64 |
65 | # Java options
66 | COPY files/setenv.sh ${CATALINA_HOME}/bin/setenv.sh
67 |
68 | # server.xml fixup
69 | COPY update-server-xml.sh /opt/update-server-xml.sh
70 | RUN /opt/update-server-xml.sh
71 |
72 | # Default configuration
73 | # Note: Make sure ERDDAP_flagKeyKey is set either in a runtime environment variable or in setup.xml
74 | # If a value is not set, a random value for ERDDAP_flagKeyKey will be generated at runtime.
75 | ENV ERDDAP_baseHttpsUrl="https://localhost:8443" \
76 | ERDDAP_emailEverythingTo="nobody@example.com" \
77 | ERDDAP_emailDailyReportsTo="nobody@example.com" \
78 | ERDDAP_emailFromAddress="nothing@example.com" \
79 | ERDDAP_emailUserName="" \
80 | ERDDAP_emailPassword="" \
81 | ERDDAP_emailProperties="" \
82 | ERDDAP_emailSmtpHost="" \
83 | ERDDAP_emailSmtpPort="" \
84 | ERDDAP_adminInstitution="Axiom Docker Install" \
85 | ERDDAP_adminInstitutionUrl="https://github.com/axiom-data-science/docker-erddap" \
86 | ERDDAP_adminIndividualName="Axiom Docker Install" \
87 | ERDDAP_adminPosition="Software Engineer" \
88 | ERDDAP_adminPhone="555-555-5555" \
89 | ERDDAP_adminAddress="123 Irrelevant St." \
90 | ERDDAP_adminCity="Nowhere" \
91 | ERDDAP_adminStateOrProvince="AK" \
92 | ERDDAP_adminPostalCode="99504" \
93 | ERDDAP_adminCountry="USA" \
94 | ERDDAP_adminEmail="nobody@example.com"
95 |
96 | COPY entrypoint.sh datasets.d.sh /
97 | ENTRYPOINT ["/entrypoint.sh"]
98 |
99 | EXPOSE 8080
100 | CMD ["catalina.sh", "run"]
101 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ERDDAP on Docker
2 |
3 | A feature full Tomcat (SSL over APR, etc.) running [ERDDAP](http://coastwatch.pfeg.noaa.gov/erddap/index.html)
4 |
5 | Most recent versions:
6 |
7 | * `axiom/docker-erddap:latest-jdk21-openjdk` (2.25.1)
8 | * `axiom/docker-erddap:2.25.1-jdk21-openjdk`
9 | * `axiom/docker-erddap:2.24-jdk21-openjdk`
10 | * `axiom/docker-erddap:2.23-jdk17-openjdk`
11 |
12 | See all versions available [here](https://hub.docker.com/r/axiom/docker-erddap/tags). As always, consult the [ERDDAP Changes](https://coastwatch.pfeg.noaa.gov/erddap/download/changes.html) documentation before upgrading your server.
13 |
14 | Use any of the `latest-*` images with caution as they follow the upstream image, and is not as thoroughly tested as tagged images.
15 |
16 | [Dependabot](https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/keeping-your-dependencies-updated-automatically) is used to automatically make PRs to update the upstream image ([`.github/dependabot.yml`](.github/dependabot.yml)).
17 |
18 | ## Quickstart
19 |
20 | ```bash
21 | docker run -d -p 8080:8080 axiom/docker-erddap:latest-jdk21-openjdk
22 | ```
23 |
24 | ## Running ERDDAP CLI Tools
25 |
26 | ### GenerateDatasetsXml
27 |
28 | ```bash
29 | docker run --rm -it \
30 | -v $(pwd)/logs:/erddapData/logs \
31 | --workdir /usr/local/tomcat/webapps/erddap/WEB-INF \
32 | axiom/docker-erddap:latest \
33 | bash GenerateDatasetsXml.sh -verbose
34 | ```
35 |
36 | or, generate a basic dataset configuration without input for
37 | later customization
38 |
39 | ```bash
40 | ./make-dataset.xml /path/to/your.csv EDDTableFromAsciiFiles > /path/to/your-dataset.xml
41 | ```
42 |
43 | ## Configuration
44 |
45 | ### Tomcat
46 |
47 | See [these instructions for configuring Tomcat](https://github.com/unidata/tomcat-docker) from the Tomcat image this image borrows from (`unidata/tomcat-docker`).
48 |
49 | ### CORS
50 |
51 | The [Tomcat configuration](https://github.com/unidata/tomcat-docker) used by this image enables the
52 | [Apache Tomcat CORS filter](https://tomcat.apache.org/tomcat-8.5-doc/config/filter.html#CORS_Filter) by
53 | default. To disable it (maybe you want to handle CORS uniformly in a proxying webserver?), set environment
54 | variable `DISABLE_CORS` to `1`.
55 |
56 | ### ERDDAP
57 |
58 | Any number of these options can be taken to configure your ERDDAP container instance to your liking.
59 |
60 | 1. Mount your own `content/erddap` directory:
61 |
62 | ```bash
63 | docker run \
64 | -p 8080:8080 \
65 | -v /path/to/your/erddap/directory:/usr/local/tomcat/content/erddap \
66 | ... \
67 | axiom/docker-erddap
68 | ```
69 |
70 | Your content directory should contain a [setup.xml](http://coastwatch.pfeg.noaa.gov/erddap/download/setup.html#setup.xml) and [dataset.xml](http://coastwatch.pfeg.noaa.gov/erddap/download/setupDatasetsXml.html) file. It can also include CSS assets that you reference in your custom `setup.xml` file.
71 |
72 | If you just want to change [setup.xml](http://coastwatch.pfeg.noaa.gov/erddap/download/setup.html#setup.xml) and [dataset.xml](http://coastwatch.pfeg.noaa.gov/erddap/download/setupDatasetsXml.html), you can mount them individually:
73 |
74 | ```bash
75 | $ docker run \
76 | -p 8080:8080 \
77 | -v /path/to/your/setup.xml:/usr/local/tomcat/content/erddap/setup.xml \
78 | -v /path/to/your/datasets.xml:/usr/local/tomcat/content/erddap/datasets.xml \
79 | ... \
80 | axiom/docker-erddap
81 | ```
82 |
83 | **If you mount setup.xml file make sure to set `/erddapData/`**
84 |
85 | 2. Configure using environmental variables
86 |
87 | You can set environmental variables to configure ERDDAP's `setup.xml` since version 2.14. See the [ERDDAP documentation](https://coastwatch.pfeg.noaa.gov/erddap/download/setup.html#setupEnvironmentVariables) for details. This can be very useful so you don't need to mount a custom `setup.xml` file into your container. If taking this approach you should look into setting the following ERDDAP config options:
88 |
89 | * `ERDDAP_baseUrl`
90 | * `ERDDAP_baseHttpsUrl`
91 | * `ERDDAP_flagKeyKey`
92 | * `ERDDAP_emailEverythingTo`
93 | * `ERDDAP_emailFromAddress`
94 | * `ERDDAP_emailUserName`
95 | * `ERDDAP_emailPassword`
96 | * `ERDDAP_emailSmtpHost`
97 | * `ERDDAP_emailSmtpPort`
98 | * `ERDDAP_adminInstitution`
99 | * `ERDDAP_adminInstitutionUrl`
100 | * `ERDDAP_adminIndividualName`
101 | * `ERDDAP_adminPosition`
102 | * `ERDDAP_adminPhone`
103 | * `ERDDAP_adminAddress`
104 | * `ERDDAP_adminCity`
105 | * `ERDDAP_adminStateOrProvince`
106 | * `ERDDAP_adminPostalCode`
107 | * `ERDDAP_adminCountry`
108 | * `ERDDAP_adminEmail`
109 |
110 | For example:
111 |
112 | ```bash
113 | docker run \
114 | -p 8080:8080 \
115 | -e ERDDAP_baseURL="http://localhost:8080" \
116 | -e ERDDAP_adminEmail="set_via_container_env@example.com" \
117 | axiom/docker-erddap
118 | ```
119 |
120 | **Depending on your container environment, it may pass in it's own environment variables relating to your resources. Potentially there could be a collision with the `ERDDAP_*` config variables if any of your resources start with ERDDAP.**
121 |
122 | 3. Configure using a shell script
123 |
124 | You can mount a file called `config.sh` to `${CATALINA_HOME}/bin/config.sh` that sets any ERDDAP configuration environmental variables you want to use. This is sourced in the container-provided `setenv.sh` file and and all variables will be exported to be used by ERDDAP for configuration. These will take precedence over environmental variable specified when running the container (see above).
125 |
126 | ```bash
127 | $ docker run \
128 | -p 8080:8080 \
129 | -e ERDDAP_adminEmail="overridden_by_config_file@example.com" \
130 | -v /path/to/your/config.sh:/usr/local/tomcat/bin/config.sh \
131 | ... \
132 | axiom/docker-erddap
133 | ```
134 |
135 | where `config.sh` contains any of the ERDDAP environmental configuration variables:
136 |
137 | ```sh
138 | ERDDAP_adminEmail="this_is_used@example.com"
139 | ```
140 |
141 | You can set any number of configuration variables in the config.sh.
142 |
143 | ```bash
144 | ERDDAP_bigParentDirectory="/erddapData/"
145 | ERDDAP_baseUrl="http://localhost:8080"
146 | ERDDAP_baseHttpsUrl="https://localhost:8443"
147 | ERDDAP_flagKeyKey="73976bb0-9cd4-11e3-a5e2-0800200c9a66"
148 |
149 | ERDDAP_emailEverythingTo="nobody@example.com"
150 | ERDDAP_emailDailyReportTo="nobody@example.com"
151 | ERDDAP_emailFromAddress="nothing@example.com"
152 | ERDDAP_emailUserName=""
153 | ERDDAP_emailPassword=""
154 | ERDDAP_emailProperties=""
155 | ERDDAP_emailSmtpHost=""
156 | ERDDAP_emailSmtpPort=""
157 |
158 | ERDDAP_adminInstitution="Axiom Docker Install"
159 | ERDDAP_adminInstitutionUrl="https://github.com/axiom-data-science/docker-erddap"
160 | ERDDAP_adminIndividualName="Axiom Docker Install"
161 | ERDDAP_adminPosition="Software Engineer"
162 | ERDDAP_adminPhone="555-555-5555"
163 | ERDDAP_adminAddress="123 Irrelevant St."
164 | ERDDAP_adminCity="Nowhere"
165 | ERDDAP_adminStateOrProvince="AK"
166 | ERDDAP_adminPostalCode="99504"
167 | ERDDAP_adminCountry="USA"
168 | ERDDAP_adminEmail="nobody@example.com"
169 | ```
170 |
171 | 4. Mount your own `bigParentDirectory`:
172 |
173 | ```bash
174 | docker run \
175 | -p 8080:8080 \
176 | -v /path/to/your/erddap/bigParentDirectory:/erddapData \
177 | ... \
178 | axiom/docker-erddap
179 | ```
180 |
181 | This is **highly** recommended, or nothing will persist across container restarts (logs/cache/etc.)
182 |
183 | 5. Specify the amount of heap memory (using Java's `Xms` and `Xmx`) to be allocated:
184 |
185 | ``` bash
186 | docker run \
187 | -p 8080:8080 \
188 | --env ERDDAP_MEMORY=10G
189 | ... \
190 | axiom/docker-erddap
191 | ```
192 |
193 | You may also explicity set `ERDDAP_MIN_MEMORY` and `ERDDAP_MAX_MEMORY` value (these map to `Xms` and `Xmx` respectively),
194 | but generally the best practice is to set these to the same value to prevent costly heap resizing at runtime.
195 |
196 | ``` bash
197 | docker run \
198 | -p 8080:8080 \
199 | --env ERDDAP_MIN_MEMORY=8G --env ERDDAP_MAX_MEMORY=8G
200 | ... \
201 | axiom/docker-erddap
202 | ```
203 |
204 | Alternatively, you can set `ERDDAP_MAX_RAM_PERCENTAGE` set the maximum Java heap size to a percentage of the memory available to the container. This option sets the JVM option `-XX:MaxRAMPercentage`. For example, to limit the container's memory to 10GB and allow the Java heap size to use 90% of that amount:
205 |
206 | ``` bash
207 | docker run \
208 | -p 8080:8080 \
209 | --memory 10g \
210 | --env ERDDAP_MAX_RAM_PERCENTAGE=90 \
211 | ... \
212 | axiom/docker-erddap
213 | ```
214 |
215 | #### datasets.d mode - EXPERIMENTAL
216 |
217 | Typically ERDDAP is configured with a single `datasets.xml` configuration file describing all datasets to be served by ERDDAP, plus some global configuration options. This file is [described in detail in the official ERDDAP documentation](https://coastwatch.pfeg.noaa.gov/erddap/download/setupDatasetsXml.html).
218 |
219 | `docker-erddap` provides an alternative `datasets.d` mode, where `datasets.xml` `dataset` elements can be stored in separate files inside a `datasets.d` directory. At startup time, the `/datasets.d` directory is scanned for any files ending in `.xml`, and matching files are concatenated (sorted by file path inside `/datasets.d`) into a generated `datasets.xml` file (specifically, an empty `` element).
220 |
221 | In this mode, top level `datasets.xml` elements like ``, ``, etc can be configured using `ERDDAP_DATASET_*` environment variables. These behave much like the `ERDDAP_*` environment variables which affect `setup.xml` values (see the [ERDDAP docs](https://coastwatch.pfeg.noaa.gov/erddap/download/setup.html#setupEnvironmentVariables) for more details), but affect top level `datasets.xml` values instead. For example, to set the `standardLicense`:
222 |
223 | ```bash
224 | docker run -d -v $(pwd)/datasets.d:/datasets.d:ro \
225 | -e ERDDAP_DATASETS_standardLicense="Use as you wish
" \
226 | --name erddap
227 | axiom/docker-erddap
228 | ```
229 |
230 | Note that in this mode, the `datasets.xml` file in the ERDDAP content directory (`/usr/local/tomcat/content/erddap`) is replaced by the generated `datasets.xml`. A backup of the original `datasets.xml` is created if one doesn't already exist.
231 |
232 | Consequently, when using `datasets.d` mode it is not necessary to mount the ERDDAP content directory at all. The contents of `datasets.d` provide all of the dataset configuration, and any top level `datasets.xml` configuration is performed through `ERDDAP_DATASETS_* env vars.
233 |
234 | For an example of running with `datasets.d` mode, see the docker-compose example in [examples](./examples).
235 |
236 | Generation of `datasets.xml` is handled in a script (`datasets.d.sh`) which prints to stdout and can be tested outside of `docker-erddap` initialization.
237 |
238 | Example:
239 |
240 | ```shell
241 | ERDDAP_DATASETS_cacheMinutes=20 ./datasets.d.sh -d examples/datasets.d
242 | ```
243 |
244 | ##### Elegantly removing datasets in datasets.d mode
245 |
246 | ERDDAP has a [specific process](https://coastwatch.pfeg.noaa.gov/erddap/download/setupDatasetsXml.html#active)
247 | to remove a previously served dataset:
248 |
249 | * Edit the dataset's `datasets.xml` element and set the `active` attribute to `false`.
250 | * Allow ERDDAP to detect the inactive dataset on the next update (or set a reload flag detect the change immediately)
251 | * Once ERDDAP has removed the dataset, remove the dataset's `datasets.xml` element (or leave as-is with `active="false"`)
252 |
253 | Failure to follow this process will result in "orphan" datasets in the ERDDAP configuration.
254 |
255 | To allow `datasets.d` mode to automatically detect removed datasets (dataset ids in the running ERDDAP configuration but
256 | not present in the newly generated `datasets.xml`), you can set environment variable `DATASETSD_MARK_REMOVED_DATASETS_INACTIVE=1`
257 | or pass the `-i` flag to `./datasets.d.sh` when running manually. This behavior may become the default in the future.
258 |
259 | #### Initialization scripts (/init.d) - EXPERIMENTAL
260 |
261 | Additional configuration can be performed by placing executable files and/or shell scripts in `/init.d`. These executables will be run on every container start up, so they **must be idempotent**. This functionality is inspired by the postgres Docker image's [`/docker-entrypoint-initdb.d`](https://github.com/docker-library/docs/blob/master/postgres/README.md#initialization-scripts).
262 |
263 | Example:
264 |
265 | ```shell
266 | #remove .hdf and .nc files from range request exclusion
267 | mkdir -p init.d
268 | cat << 'EOF' > init.d/10-remove-hdf-nc-range-request-exclusion.sh
269 | sed -i 's/.hdf, .nc, //g' ${CATALINA_HOME}/webapps/erddap/WEB-INF/classes/gov/noaa/pfel/erddap/util/messages.xml
270 | EOF
271 |
272 | chmod +x init.d/10-remove-hdf-nc-range-request-exclusion.sh
273 |
274 | docker run -d -p 8080:8080 -v $(pwd)/init.d:/init.d:ro --name erddap axiom/docker-erddap
275 | ```
276 |
277 | #### Log Consolidation - EXPERIMENTAL
278 |
279 | ERDDAP writes logs to a `logs/log.txt` file relative to ERDDAP's `bigParentDirectory`. The log format doesn't adhere to a standard logging format and isn't easily parsable. The logs also don't provide timestamps for when the logs messages were written. To enhance the logging experience when using this docker image you can run a sidecar `rsyslog` container that will:
280 |
281 | * Consolidate the log files from ERDDAP and Tomcat (both application and access)
282 | * Add a timestamp to the ERDDAP logs
283 | * Filter out some ERDDAP log "noise" (opinionated)
284 | * Send the consolidated and filtered log messages to `stdout`
285 |
286 | For an example of running with a sidecar `rsyslog` container, see the docker-compose example in [examples](./examples). The supporting `rsyslog` configuration files are located in [rsyslog](./examples/rsyslog). Please note that this requires both the ERDDAP `bigParentDirectory` and Tomcat's log directory to be bind mounted to the host from the ERDDAP container or managed in Docker named volumes mounted to both the ERDDAP and rsyslog containers.
287 |
288 | Example consolidated log:
289 |
290 | ```log
291 | erddap-rsyslogd_1 | [TOMCAT] 08-Jul-2022 04:44:14.004 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["http-nio-8080"]
292 | erddap-rsyslogd_1 | [TOMCAT] 08-Jul-2022 04:44:14.011 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in 3582 ms
293 | ...
294 | erddap-rsyslogd_1 | [ERDDAP] 2022-07-08T04:44:19Z Major LoadDatasets Time Series: MLD Datasets Loaded Requests (median times in ms) Number of Threads MB Open
295 | erddap-rsyslogd_1 | timestamp time nTry nFail nTotal nSuccess (median) nFail (median) memFail tooMany tomWait inotify other inUse Files
296 | erddap-rsyslogd_1 | ---------------------------- ----- ----------------- ------------------------------------------------ --------------------- ----- -----
297 | erddap-rsyslogd_1 | 2022-07-08T04:44:18+00:00 1s 1 0 2 1 ( 8) 0 ( 0) 0 0 10 1 17 44 0%
298 | ...
299 | erddap-rsyslogd_1 | [ACCESS] 127.0.0.1 - - [08/Jul/2022:04:44:17 +0000] "GET /erddap/index.html HTTP/1.1" 200 25268
300 | erddap-rsyslogd_1 | [ACCESS] 127.0.0.1 - - [08/Jul/2022:04:44:27 +0000] "GET /erddap/index.html HTTP/1.1" 200 25268
301 | ```
302 |
--------------------------------------------------------------------------------
/datasets.d.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | #datasets.d datasets.xml generator
3 | #If /datasets.d exists, concatenate all *.xml files found in the
4 | #hierarchy into a generated datasets.xml file. Scan for environment
5 | #variables prefixed with ERDDAP_DATASETS_ and use the values to set
6 | #top level datasets.xml elements (e.g. cacheMinutes)
7 | #See ERDDAP docs at https://coastwatch.pfeg.noaa.gov/erddap/download/setupDatasetsXml.html
8 | #for a thorough description of the datasets.xml format.
9 |
10 | DATASETSD_DIR="${DATASETSD_DIR:-/datasets.d}"
11 | DATASETSD_OUTPUT_PATH="${DATASETSD_OUTPUT_PATH:-/usr/local/tomcat/content/erddap/datasets.xml}"
12 | DATASETSD_MARK_REMOVED_DATASETS_INACTIVE="${DATASETSD_MARK_REMOVED_DATASETS_INACTIVE:-0}"
13 | DATASETSD_WRITE_TO_OUTPUT_PATH="${DATASETSD_WRITE_TO_OUTPUT_PATH:-0}"
14 | DATASETSD_REFRESH_MISSING_DATASETS="${DATASETSD_REFRESH_MISSING_DATASETS:-0}"
15 | while getopts ":d:io:rw" opt; do
16 | case ${opt} in
17 | d )
18 | DATASETSD_DIR="$OPTARG"
19 | ;;
20 | i )
21 | DATASETSD_MARK_REMOVED_DATASETS_INACTIVE=1
22 | ;;
23 | o )
24 | DATASETSD_OUTPUT_PATH="$OPTARG"
25 | ;;
26 | r )
27 | DATASETSD_REFRESH_MISSING_DATASETS=1
28 | ;;
29 | w )
30 | DATASETSD_WRITE_TO_OUTPUT_PATH=1
31 | ;;
32 | \? )
33 | echo "Invalid option: $OPTARG" 1>&2
34 | ;;
35 | : )
36 | echo "Invalid option: $OPTARG requires an argument" 1>&2
37 | ;;
38 | esac
39 | done
40 | shift $((OPTIND -1))
41 |
42 | if [ ! -d "${DATASETSD_DIR}" ]; then
43 | echo "${DATASETSD_DIR} doesn't exist or isn't a directory, exiting" >&2
44 | exit 1
45 | fi
46 |
47 | #generate datasets.xml content from constituent datasets fragments in datasets.d directory
48 | #run dataset fragments through xmlstarlet to remove xml declarations and catch other problems
49 | DXML=$(echo "$(find ""${DATASETSD_DIR}"" -name '*.xml' -type f -print0 | sort -z | xargs -0 xmlstarlet edit --omit-decl)")
50 |
51 | #set top level datasets.xml config with ERDDAP_DATASETS_* env vars
52 | while read -r e; do
53 | k=$(echo "$e" | cut -d= -f1);
54 | v=$(echo "$e" | cut -d= -f2-);
55 | DXML=$(echo "$DXML" | xmlstarlet edit --inplace --subnode /erddapDatasets --type elem --name "$k" --value "$v")
56 | done < <(env | grep -oP '(?<=^ERDDAP_DATASETS_).*')
57 |
58 | #mark removed datasets as inactive
59 | if [ "$DATASETSD_MARK_REMOVED_DATASETS_INACTIVE" == "1" ] && [ -n "$DATASETSD_OUTPUT_PATH" ] && [ -f "$DATASETSD_OUTPUT_PATH" ]; then
60 | #previous version of the target datasets.xml file exists,
61 | #if any datasets there not marked inactive are missing from our generated file
62 | #add them as inactive datasets so ERDDAP can cleanly make them inactive
63 | #https://coastwatch.pfeg.noaa.gov/erddap/download/setupDatasetsXml.html#active
64 | while read -r inactive_dataset; do
65 | DXML=$(echo "$DXML" | xmlstarlet edit -P -s /erddapDatasets --type elem --name inactiveDataset -v "" \
66 | -i //inactiveDataset -t attr -n "datasetID" -v "${inactive_dataset}" \
67 | -i //inactiveDataset -t attr -n "active" -v "false" \
68 | -r //inactiveDataset -v dataset)
69 | done < <(comm -13 <(echo "$DXML" | xmlstarlet select -t -v "//erddapDatasets/dataset/@datasetID" | sort) \
70 | <(<"$DATASETSD_OUTPUT_PATH" xmlstarlet select -t -v "//erddapDatasets/dataset[not(@active='false')]/@datasetID" | sort))
71 | true
72 | fi
73 |
74 | #empty edit for formatting
75 | DXML=$(echo "$DXML" | xmlstarlet edit --inplace)
76 |
77 | #write output to target file if one was provided and write to stdout flag was not provided
78 | if [ -n "$DATASETSD_OUTPUT_PATH" ] && [ "$DATASETSD_WRITE_TO_OUTPUT_PATH" == "1" ]; then
79 | echo "$DXML" > "$DATASETSD_OUTPUT_PATH"
80 | #set refresh flags for any datasetIDs in datasets.xml that are not in the running ERDDAP config if the refresh option was set
81 | if [ -n "$DATASETSD_REFRESH_MISSING_DATASETS" ] && [ "$DATASETSD_REFRESH_MISSING_DATASETS" == "1" ]; then
82 | comm -23 \
83 | <(xmlstarlet select -t -v "/erddapDatasets/dataset/@datasetID" "$DATASETSD_OUTPUT_PATH" | sort) \
84 | <(curl -sS "http://localhost:8080/erddap/tabledap/allDatasets.csv0?datasetID" | grep -v "^allDatasets$" | sort) \
85 | | xargs -I{} touch /erddapData/flag/{}
86 | fi
87 | else
88 | echo "$DXML"
89 | fi
90 |
--------------------------------------------------------------------------------
/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 |
4 | # preferable to fire up Tomcat via start-tomcat.sh which will start Tomcat with
5 | # security manager, but inheriting containers can also start Tomcat via
6 | # catalina.sh
7 |
8 | if [ "$1" = 'start-tomcat.sh' ] || [ "$1" = 'catalina.sh' ]; then
9 | # generate random flagKeyKey if not set
10 | if [ -z "$ERDDAP_flagKeyKey" ] && grep "CHANGE THIS TO YOUR FAVORITE QUOTE" \
11 | "${CATALINA_HOME}/content/erddap/setup.xml" &> /dev/null; then
12 | echo "flagKeyKey isn't properly set. Generating a random value." >&2
13 | export ERDDAP_flagKeyKey=$(cat /proc/sys/kernel/random/uuid)
14 | fi
15 |
16 | USER_ID=${TOMCAT_USER_ID:-1000}
17 | GROUP_ID=${TOMCAT_GROUP_ID:-1000}
18 |
19 | ###
20 | # Tomcat user
21 | ###
22 | # create group for GROUP_ID if one doesn't already exist
23 | if ! getent group $GROUP_ID &> /dev/null; then
24 | groupadd -r tomcat -g $GROUP_ID
25 | fi
26 | # create user for USER_ID if one doesn't already exist
27 | if ! getent passwd $USER_ID &> /dev/null; then
28 | useradd -u $USER_ID -g $GROUP_ID tomcat
29 | fi
30 | # alter USER_ID with nologin shell and CATALINA_HOME home directory
31 | usermod -d "${CATALINA_HOME}" -s /sbin/nologin $(id -u -n $USER_ID)
32 |
33 | ###
34 | # Change CATALINA_HOME ownership to tomcat user and tomcat group
35 | # Restrict permissions on conf
36 | ###
37 |
38 | chown -R $USER_ID:$GROUP_ID ${CATALINA_HOME} && find ${CATALINA_HOME}/conf \
39 | -type d -exec chmod 755 {} \; -o -type f -exec chmod 400 {} \;
40 | chown -R $USER_ID:$GROUP_ID /erddapData
41 | sync
42 |
43 | ###
44 | # Deactivate CORS filter in web.xml if DISABLE_CORS=1
45 | # Useful if CORS is handled outside of Tomcat (e.g. in a proxying webserver like nginx)
46 | ###
47 | if [ "$DISABLE_CORS" == "1" ]; then
48 | echo "Deactivating Tomcat CORS filter"
49 | xmlstarlet edit --inplace --delete '//_:filter[./_:filter-name = "CorsFilter"]' \
50 | --delete '//_:filter-mapping[./_:filter-name = "CorsFilter"]' "${CATALINA_HOME}/conf/web.xml"
51 | fi
52 |
53 | ###
54 | # Add datasets in /datasets.d to datasets.xml
55 | ###
56 | if [ -d "/datasets.d" ]; then
57 | echo "Creating datasets.xml from /datasets.d"
58 | ERDDAP_CONTENT_DIR="/usr/local/tomcat/content/erddap"
59 | DATASETS_XML="${ERDDAP_CONTENT_DIR}/datasets.xml"
60 | if [ -f "$DATASETS_XML" ]; then
61 | #datasets.xml exists, make sure we have a backup of it
62 | DATASETS_XML_MD5SUM=$(md5sum "$DATASETS_XML" | awk '{print $1}')
63 | if ! md5sum "${ERDDAP_CONTENT_DIR}/datasets.xml.*.bak" 2>/dev/null | grep -q "$DATASETS_XML_MD5SUM"; then
64 | #we don't have a backup of this version of datasets.xml yet, make one
65 | DATASETS_XML_BACKUP="${ERDDAP_CONTENT_DIR}"/datasets.xml.$(date -u +"%Y%m%dT%H%M%SZ").bak
66 | echo "Backing up "${DATASETS_XML}" to ${DATASETS_XML_BACKUP}"
67 | cp "$DATASETS_XML" "${DATASETS_XML_BACKUP}"
68 | fi
69 | fi
70 | /datasets.d.sh -o "$DATASETS_XML" -w
71 | fi
72 |
73 | ###
74 | # Run executables/shell scripts in /init.d on each container startup
75 | # Inspired by postgres' /docker-entrypoint-initdb.d
76 | # https://github.com/docker-library/docs/blob/master/postgres/README.md#initialization-scripts
77 | # https://github.com/docker-library/postgres/blob/master/docker-entrypoint.sh#L156
78 | ###
79 | if [ -d "/init.d" ]; then
80 | for f in /init.d/*; do
81 | if [ -x "$f" ]; then
82 | echo "Executing $f"
83 | "$f"
84 | elif [[ $f == *.sh ]]; then
85 | echo "Sourcing $f (not executable)"
86 | . "$f"
87 | fi
88 | done
89 | fi
90 |
91 | exec gosu $USER_ID "$@"
92 | fi
93 |
94 | exec "$@"
95 |
--------------------------------------------------------------------------------
/examples/data/trees.csv:
--------------------------------------------------------------------------------
1 | "Index", "Girth (in)", "Height (ft)", "Volume(ft^3)"
2 | 1, 8.3, 70, 10.3
3 | 2, 8.6, 65, 10.3
4 | 3, 8.8, 63, 10.2
5 | 4, 10.5, 72, 16.4
6 | 5, 10.7, 81, 18.8
7 | 6, 10.8, 83, 19.7
8 | 7, 11.0, 66, 15.6
9 | 8, 11.0, 75, 18.2
10 | 9, 11.1, 80, 22.6
11 | 10, 11.2, 75, 19.9
12 | 11, 11.3, 79, 24.2
13 | 12, 11.4, 76, 21.0
14 | 13, 11.4, 76, 21.4
15 | 14, 11.7, 69, 21.3
16 | 15, 12.0, 75, 19.1
17 | 16, 12.9, 74, 22.2
18 | 17, 12.9, 85, 33.8
19 | 18, 13.3, 86, 27.4
20 | 19, 13.7, 71, 25.7
21 | 20, 13.8, 64, 24.9
22 | 21, 14.0, 78, 34.5
23 | 22, 14.2, 80, 31.7
24 | 23, 14.5, 74, 36.3
25 | 24, 16.0, 72, 38.3
26 | 25, 16.3, 77, 42.6
27 | 26, 17.3, 81, 55.4
28 | 27, 17.5, 82, 55.7
29 | 28, 17.9, 80, 58.3
30 | 29, 18.0, 80, 51.5
31 | 30, 18.0, 80, 51.0
32 | 31, 20.6, 87, 77.0
33 |
34 |
--------------------------------------------------------------------------------
/examples/datasets.d/trees.csv.xml:
--------------------------------------------------------------------------------
1 |
2 | 10080
3 | 10000
4 | /data/
5 | trees.csv
6 | false
7 |
8 | last
9 | 0
10 | ISO-8859-1
11 |
12 | 1
13 | 2
14 |
15 |
16 | false
17 |
18 | Other
19 | COARDS, CF-1.6, ACDD-1.3
20 | https://people.sc.fsu.edu/~jburkardt/data/csv/trees.csv
21 | ???
22 | tree, data, ft^3, girth, height, index, local, source, volume, Volume_ft_3
23 | [standard]
24 | (local files)
25 | CF Standard Name Table v70
26 | Generated dataset for trees.csv
27 | trees.csv
28 |
29 |
30 | Index
31 | Index
32 | byte
33 |
34 | 127
35 | Unknown
36 | Index
37 |
38 |
39 |
40 | Girth (in)
41 | Girth
42 | float
43 |
44 | Unknown
45 | Girth (in)
46 | in
47 |
48 |
49 |
50 | Height (ft)
51 | Height
52 | byte
53 |
54 | 127
55 | Unknown
56 | Height (ft)
57 | ft
58 |
59 |
60 |
61 | Volume(ft^3)
62 | Volume
63 | float
64 |
65 | Unknown
66 | Volume(ft^3)
67 | ft^3
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/examples/docker-compose.yml:
--------------------------------------------------------------------------------
1 | services:
2 | erddap:
3 | build: ..
4 | ports:
5 | - "${ERDDAP_HTTP_PORT:-8888}:8080"
6 | environment:
7 | ERDDAP_baseUrl: "${ERDDAP_baseUrl:-http://localhost:8888}"
8 | ERDDAP_DATASETS_standardPrivacyPolicy: "Any and all usage of this data is permitted.
"
9 | DATASETSD_MARK_REMOVED_DATASETS_INACTIVE: "1"
10 | DATASETSD_REFRESH_MISSING_DATASETS: "1"
11 | DISABLE_CORS: "1"
12 | volumes:
13 | - ./data:/data:ro
14 | - ./datasets.d:/datasets.d:ro
15 | - ./erddapData:/erddapData
16 | - ./tomcatLogs:/usr/local/tomcat/logs
17 | #healthcheck to check ERDDAP landing page. the check provides the added bonus
18 | #of triggering ERDDAP initialization before the first user visit
19 | healthcheck:
20 | test: ["CMD", "curl", "-f", "http://localhost:8080/erddap/index.html"]
21 | interval: 10s
22 | erddap-logs:
23 | image: gynter/rsyslog-relp:alpine
24 | volumes:
25 | - ./erddapData:/erddapData:ro
26 | - ./tomcatLogs:/usr/local/tomcat/logs:ro
27 | - ./rsyslog/rsyslog.conf:/etc/rsyslog.conf:ro
28 | - ./rsyslog/rsyslog.d:/etc/rsyslog.d:ro
29 | depends_on:
30 | - erddap
31 |
--------------------------------------------------------------------------------
/examples/rsyslog/rsyslog.conf:
--------------------------------------------------------------------------------
1 | #
2 | # Docker RSyslog (https://github.com/gynter/docker-rsyslog)
3 | #
4 | # This configuration is designed to run securely on non-systemd (i.e Alpine) containers as UID/GID 1000.
5 | # It's a generic configuration file which can be extended via including extra configuration files from /etc/rsyslog.d/
6 | # directory.
7 | #
8 | # See https://github.com/gynter/rsyslog-docker-compose-example for deployment example using Docker Compose.
9 | #
10 | # This configuration file uses advanced (RainerScript) and basic format.
11 | # See https://www.rsyslog.com/doc/v8-stable/configuration/conf_formats.html#which-format-should-i-use for more
12 | # information. Using obsolete legacy syntax is strongly discouraged!
13 | # See https://www.rsyslog.com/doc/master/rainerscript/ for more information.
14 | #
15 | # If you experience problems, see https://www.rsyslog.com/doc/troubleshoot.html
16 |
17 | ####################################
18 | #### Global directives ####
19 | ####################################
20 |
21 | # https://www.rsyslog.com/doc/v8-stable/rainerscript/global.html
22 | global(
23 | # Sets the directory that rsyslog uses for work files, e.g. imfile state or queue spool files.
24 | workDirectory="/srv/rsyslog"
25 |
26 | # Sets the rsyslogd process’ umask. If not specified, the system-provided default is used. The value given must always
27 | # be a 4-digit octal number, with the initial digit being zero.
28 | umask="0027"
29 |
30 | # If set to off (legacy default to remain compatible to sysklogd), the domain part from a name that is within the same
31 | # domain as the receiving system is stripped. If set to on, full names are always used.
32 | preserveFQDN="on"
33 |
34 | # Configures the maximum message size allowed for all inputs. Default is 8K. Anything above the maximum size will be
35 | # truncated.
36 | # Note: some modules provide separate parameters that allow overriding this setting (e.g., imrelp’s MaxDataSize
37 | # parameter).
38 | maxMessageSize="8K"
39 |
40 | # If set to “on”, rsyslogd can be terminated by pressing ctl-c. This is most useful for containers. If set to “off”
41 | # (the default), this is not possible.
42 | shutdown.enable.ctlc="on"
43 |
44 | # This controls whether slashes in the “programname” property (the static part of the tag) are permitted or not. By
45 | # default this is not permitted, but some Linux tools (including most importantly the journal) store slashes as part
46 | # of the program name inside the syslogtag. In those cases, the programname is truncated at the first slash.
47 | #
48 | # In other words, if the setting is off, a value of app/foo[1234] in the tag will result in a programname of app, and
49 | # if an application stores an absolute path name like /app/foo[1234], the programname property will be empty (“”).
50 | # If set to on, a syslogtag of /app/foo[1234] will result in a programname value of /app/foo and a syslogtag of
51 | # app/foo[1234] will result in a programname value of app/foo.
52 | parser.permitSlashInProgramName="on"
53 |
54 | # This parameter instructs rsyslogd to replace control characters during reception of the message. The intent is to
55 | # provide a way to stop non-printable messages from entering the syslog system as whole. If this option is turned on,
56 | # all control-characters are converted to a 3-digit octal number and be prefixed with the
57 | # parser.controlCharacterEscapePrefix character (being ‘#’ by default). For example, if the BEL character (ctrl-g) is
58 | # included in the message, it would be converted to ‘#007’. To be compatible to sysklogd, this option must be turned
59 | # on.
60 | #
61 | # Warning:
62 | # - turning on this option most probably destroys non-western character sets (like Japanese, Chinese and Korean);
63 | # - turning on this option destroys digital signatures if such exists inside the message;
64 | # - if turned on, the drop-cc, space-cc and escape-cc property replacer options do not work as expected because
65 | # control characters are already removed upon message reception. If you intend to use these property replacer
66 | # options, you must turn off parser.escapeControlCharactersOnReceive.
67 | parser.escapeControlCharactersOnReceive="off"
68 |
69 | # This parameter permits to prevent rsyslog from running when the configuration file is not clean. “Not Clean” means
70 | # there are errors or some other annoyances that rsyslogd reports on startup. Note that with the current code base it
71 | # is not always possible to differentiate between an real error and a warning-like condition. As such, the startup
72 | # will also prevented if warnings are present. I consider this a good thing in being “strict”, but I admit there also
73 | # currently is no other way of doing it.
74 | abortOnUncleanConfig="on"
75 | )
76 |
77 | ####################################
78 | #### Modules ####
79 | ####################################
80 |
81 | # Provides module for writing messages to stdout.
82 | # https://www.rsyslog.com/doc/master/configuration/modules/omstdout.html
83 | module(load="omstdout")
84 |
85 | ####################################
86 | #### Rules ####
87 | ####################################
88 |
89 | # Basic syntax is used here.
90 |
91 | # Send own messages to Docker console.
92 | syslog.* :omstdout:
93 |
94 | # /END Basic syntax is used here.
95 |
96 | ####################################
97 | #### Other ####
98 | ####################################
99 |
100 | # Include all *.conf files in /etc/rsyslog.d/.
101 | # https://www.rsyslog.com/doc/v8-stable/rainerscript/include.html
102 | include(
103 | file="/etc/rsyslog.d/*.conf"
104 | mode="optional"
105 | )
106 |
--------------------------------------------------------------------------------
/examples/rsyslog/rsyslog.d/00-imports.conf:
--------------------------------------------------------------------------------
1 | module(load="imfile")
2 |
--------------------------------------------------------------------------------
/examples/rsyslog/rsyslog.d/10-tomcat.conf:
--------------------------------------------------------------------------------
1 | input(
2 | type="imfile"
3 | File="/usr/local/tomcat/logs/*.log"
4 | Tag="tomcat"
5 | Ruleset="tomcatToStdOut"
6 | readMode="0"
7 | escapeLF="on"
8 | )
9 |
10 | input(
11 | type="imfile"
12 | File="/usr/local/tomcat/logs/localhost_access_log*"
13 | Tag="access"
14 | Ruleset="tomcatToStdOut"
15 | )
16 |
17 | template(name="tomcatStdOut" type="list") {
18 | constant(value="[")
19 | property(name="syslogtag" caseconversion="upper")
20 | constant(value="] ")
21 | property(name="msg" droplastlf="on" )
22 | constant(value="\n")
23 | }
24 |
25 | ruleset(name="tomcatToStdOut") {
26 | action(
27 | type="omstdout" template="tomcatStdOut"
28 | )
29 | }
30 |
--------------------------------------------------------------------------------
/examples/rsyslog/rsyslog.d/20-erddap.conf:
--------------------------------------------------------------------------------
1 | input(
2 | type="imfile"
3 | File="/erddapData/logs/log.txt"
4 | Tag="erddap"
5 | Ruleset="erddapToStdOut"
6 | readMode="1"
7 | escapeLF="off"
8 | )
9 |
10 | template(name="erddapStdOut" type="list") {
11 | constant(value="[")
12 | property(name="syslogtag" caseconversion="upper")
13 | constant(value="] ")
14 | property(name="timestamp" dateformat="year" date.inUTC="on")
15 | constant(value="-")
16 | property(name="timestamp" dateformat="month" date.inUTC="on")
17 | constant(value="-")
18 | property(name="timestamp" dateformat="day" date.inUTC="on")
19 | constant(value="T")
20 | property(name="timestamp" dateformat="hour" date.inUTC="on")
21 | constant(value=":")
22 | property(name="timestamp" dateformat="minute" date.inUTC="on")
23 | constant(value=":")
24 | property(name="timestamp" dateformat="second" date.inUTC="on")
25 | constant(value="Z ")
26 | property(name="msg" droplastlf="on" )
27 | constant(value="\n")
28 | }
29 |
30 | ruleset(name="erddapToStdOut") {
31 | # Header/footer for log sections can be ignored
32 | if $msg startswith '\\\\\\\\****' then stop
33 | if $msg startswith '{{{' then stop
34 | if $msg startswith '}}}' then stop
35 |
36 | # Prevents the statistics on response from being logged since
37 | # this is better obtained from the ERDDAP status webpage
38 | if $msg contains 'Time Distribution' then stop
39 | if $msg contains 'Times Distribution' then stop
40 | if $msg contains 'TaskThread' then stop
41 |
42 | # Not sure what these mean and are noisy
43 | if $msg startswith 'Language (' then stop
44 |
45 | # The log messages explaining what each Thread is doing
46 | if $msg contains ' Thread[' then stop
47 | action(
48 | type="omstdout" template="erddapStdOut"
49 | )
50 | }
51 |
--------------------------------------------------------------------------------
/files/setenv.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | if [ -f "${CATALINA_HOME}/bin/config.sh" ];
4 | then
5 | set -o allexport
6 | source "${CATALINA_HOME}/bin/config.sh"
7 | set +o allexport
8 | fi
9 |
10 | ERDDAP_CONFIG=$(env | grep --regexp "^ERDDAP_.*$" | sort)
11 | if [ -n "$ERDDAP_CONFIG" ]; then
12 | echo -e "ERDDAP configured with:\n$ERDDAP_CONFIG"
13 | fi
14 |
15 | JAVA_MAJOR_VERSION=$(java -version 2>&1 | head -1 | cut -d'"' -f2 | sed '/^1\./s///' | cut -d'.' -f1)
16 |
17 | # JAVA_OPTS
18 | NORMAL="-server"
19 |
20 | # Memory
21 | if [ -n "$ERDDAP_MAX_RAM_PERCENTAGE" ]; then
22 | JVM_MEMORY_ARGS="-XX:MaxRAMPercentage=${ERDDAP_MAX_RAM_PERCENTAGE}"
23 | else
24 | ERDDAP_MEMORY="${ERDDAP_MEMORY:-4G}"
25 | JVM_MEMORY_ARGS="-Xms${ERDDAP_MIN_MEMORY:-${ERDDAP_MEMORY}} -Xmx${ERDDAP_MAX_MEMORY:-${ERDDAP_MEMORY}}"
26 | fi
27 |
28 | HEAP_DUMP="-XX:+HeapDumpOnOutOfMemoryError"
29 | HEADLESS="-Djava.awt.headless=true"
30 |
31 | EXTRAS=${JAVA_EXTRAS:-}
32 | if [ $JAVA_MAJOR_VERSION -lt 9 ]; then
33 | #these options are deprecated in java 9 and illegal in java 14+
34 | EXTRAS="$EXTRAS -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled"
35 | fi
36 |
37 | CONTENT_ROOT="-DerddapContentDirectory=$CATALINA_HOME/content/erddap"
38 | JNA_DIR="-Djna.tmpdir=/tmp/"
39 | FASTBOOT="-Djava.security.egd=file:/dev/./urandom"
40 |
41 | JAVA_OPTS="$JAVA_OPTS $NORMAL $JVM_MEMORY_ARGS $HEAP_DUMP $HEADLESS $EXTRAS $CONTENT_ROOT/ $JNA_DIR $FASTBOOT"
42 | echo "ERDDAP Running with: $JAVA_OPTS"
43 |
--------------------------------------------------------------------------------
/init.d/10-remove-hdf-nc-range-request-exclusion.sh:
--------------------------------------------------------------------------------
1 | sed -i 's/.hdf, .nc, //g' ${CATALINA_HOME}/webapps/erddap/WEB-INF/classes/gov/noaa/pfel/erddap/util/messages.xml
2 |
--------------------------------------------------------------------------------
/make-dataset-xml.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | #Used to non-interactively generate a datasets.xml dataset configuration for ERDDAP
4 | #using GenerateDatasetsXml.sh using all default values. The output
5 | #of this script should be used as a starting point and must be populated
6 | #with the correct values for the dataset
7 | #
8 | #Example usage:
9 | #./make-datasets-xml.sh /path/to/my.csv EDDTableFromAsciiFiles
10 |
11 | TARGET_FILE="$1"
12 |
13 | if [ ! -f "$TARGET_FILE" ]; then
14 | echo "File ""$TARGET_FILE"" doesn't exist" >&2
15 | exit 1
16 | fi
17 |
18 | DATASET_TYPE=${2:-"EDDTableFromAsciiFiles"}
19 |
20 | TARGET_FILENAME="$(basename ""$TARGET_FILE"")"
21 |
22 | docker run --rm -v $(realpath "$TARGET_FILE"):/data_directory/data_file \
23 | --workdir /usr/local/tomcat/webapps/erddap/WEB-INF axiom/docker-erddap \
24 | bash ./GenerateDatasetsXml.sh ${DATASET_TYPE} /data_directory data_file $(yes '""' | head -n 18) \
25 | | sed -n '/^&2
11 | exit 1
12 | fi
13 |
14 | RELAXED_PATH_CHARS="[]|"
15 | RELAXED_QUERY_CHARS="[]:|{}^\`"<>"
16 |
17 | function set_attribute {
18 | ELEM="$1"
19 | ATTR="$2"
20 | VAL="$3"
21 | if [ -z "$(xmlstarlet sel -t -c "${ELEM}[@${ATTR}='${VAL}']" $SERVER_XML)" ]; then
22 | #xmlstarlet escapes special characters like & when writing values, and we
23 | #want the attributes to be exactly as we define them. insert replacement
24 | #target tokens instead, and then replace with sed.
25 | #ampersands are also special characters in sed, so replace with ~ first
26 | #and then replace again back to &
27 | TOKEN="__${ATTR}__"
28 | xmlstarlet edit --inplace -P -u "${ELEM}/@${ATTR}" -v "${TOKEN}" \
29 | -i "${ELEM}[not(@${ATTR})]" -t attr -n "${ATTR}" -v "${TOKEN}" \
30 | $SERVER_XML
31 | sed -i -e "s/${TOKEN}/$( echo $VAL | tr '&' '~')/" -e "s/~/\&/g" $SERVER_XML
32 | fi
33 | }
34 |
35 | #set Connector relaxedPathChars and relaxedQueryChars to allow DAP queries
36 | set_attribute /Server/Service/Connector relaxedPathChars "$RELAXED_PATH_CHARS"
37 | set_attribute /Server/Service/Connector relaxedQueryChars "$RELAXED_QUERY_CHARS"
38 |
39 | # Enable request attributes so that, when using a reverse proxy, the original
40 | # client ip is recorded in logs rather than the internal proxy ip
41 | set_attribute /Server/Service/Engine/Host/Valve requestAttributesEnable "true"
42 |
43 | #create RemoteIpValve if missing. this is needed so ERDDAP knows when its responding to https requests
44 | #end result should look like:
45 | #
49 | #https://stackoverflow.com/a/9172796/193435
50 | if [ -z "$(xmlstarlet sel -t -c "/Server/Service/Engine/Host/Valve[@className='org.apache.catalina.valves.RemoteIpValve']" $SERVER_XML)" ]; then
51 | xmlstarlet edit --inplace -P -s /Server/Service/Engine/Host -t elem -n RemoteIpValve -v "" \
52 | -i //RemoteIpValve -t attr -n "className" -v "org.apache.catalina.valves.RemoteIpValve" \
53 | -i //RemoteIpValve -t attr -n "remoteIpHeader" -v "X-Forwarded-For" \
54 | -i //RemoteIpValve -t attr -n "protocolHeader" -v "X-Forwarded-Proto" \
55 | -i //RemoteIpValve -t attr -n "protocolHeaderHttpsValue" -v "https" \
56 | -r //RemoteIpValve -v Valve \
57 | $SERVER_XML
58 | fi
59 |
--------------------------------------------------------------------------------