Skip to content
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
1 change: 1 addition & 0 deletions hols/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
* [Grafana Dashboard for Helidon](grafana/README.md)
* [LangChain4J Integration](langchain4j/README.md)
* [Using OCI Devops to deploy a Helidon application to OKE or a Compute Instance](oci-devops/README.md)
* [Deploying a Helidon OCI MP Application on a Basic OCI Setup](oci-basic-setup/README.md)
276 changes: 276 additions & 0 deletions hols/oci-basic-setup/README.md

Large diffs are not rendered by default.

146 changes: 146 additions & 0 deletions hols/oci-basic-setup/deploy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
#!/bin/bash
#
# Copyright (c) 2025 Oracle and/or its affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

# Use this to specify the JDK Installer version in tar_gz format
JDK_TAR_GZ_INSTALLER="https://download.oracle.com/java/21/latest/jdk-21_linux-x64_bin.tar.gz"

HELIDON_MP_APP_ZIP=oci-mp-server.zip
DEFAULT_PROJECT_PATH=~/oci-mp
SCRIPT_DIR=$(dirname "$0")
# shellcheck disable=SC1091
source "${SCRIPT_DIR}"/get_common.sh

# Main routine
if [ -z "${1}" ]; then
read -r -p "Enter the Helidon MP project's root directory (default: ${DEFAULT_PROJECT_PATH}): " PROJECT_PATH
# Use eval to expand ~ if it is part of the input
PROJECT_PATH=$(eval echo -n "${PROJECT_PATH:-${DEFAULT_PROJECT_PATH}}")
echo "$PROJECT_PATH"
else
PROJECT_PATH=${1}
fi
if [ ! -d "${PROJECT_PATH}" ]; then
echo "Error: \"${PROJECT_PATH}\" is not a valid directory"
exit 1
fi
CURRENT_DIR=$(pwd)
SERVER_BIN_DIR="${PROJECT_PATH}/server/target"
cd "${SERVER_BIN_DIR}" || exit 1

# Assemble the application zip
zip -r "${CURRENT_DIR}/${HELIDON_MP_APP_ZIP}" libs oci-mp-server.jar
cd "${CURRENT_DIR}" || exit 1

# Generate private key file that will be use to ssh or scp to the instance
"${SCRIPT_DIR}"/get.sh create_ssh_private_key
# Get instance public IP
PUBLIC_IP=$("${SCRIPT_DIR}"/get.sh public_ip)
# Upload the application zip
scp -o StrictHostKeyChecking=accept-new -i private.key oci-mp-server.zip opc@"${PUBLIC_IP}":/home/opc

# Download & install jdk and run app
ssh -i private.key opc@"${PUBLIC_IP}" "bash -s ${HELIDON_MP_APP_ZIP} ${JDK_TAR_GZ_INSTALLER}" << 'EOF'
HELIDON_MP_APP_ZIP=${1}
JDK_TAR_GZ_INSTALLER=${2}

# Create application and log directory
export APP_DIR=~/oci-mp/app
export LOG_DIR=~/oci-mp/log
mkdir -p "${APP_DIR}"
mkdir -p "${LOG_DIR}"

# Unzip the application binary
unzip -o "${HELIDON_MP_APP_ZIP}" -d "${APP_DIR}"
rm "${HELIDON_MP_APP_ZIP}"

# Download and extract JDK
JDK_TAR_GZ_INSTALLER_BASE=$(basename ${JDK_TAR_GZ_INSTALLER})
if ! ls "${JDK_TAR_GZ_INSTALLER_BASE}" ; then
rm -f "*jdk*.tar.gz"
rm -rf jdk*
# Download JDK
curl -O "${JDK_TAR_GZ_INSTALLER}" && echo "JDK downloaded successfully"
tar xzf ${JDK_TAR_GZ_INSTALLER_BASE} && echo "JDK installed successfully"
fi

# Create Service file
export JAVA_BIN=$(ls -d "$(pwd)"/jdk*/)bin
cat << EOF_INNER > helidon-app.service.new
[Unit]
Description=Helidon OCI-MP application service
After=syslog.target network.target

[Service]
User=opc
Type=simple
WorkingDirectory=/home/opc
ExecStart=/bin/bash -c '${JAVA_BIN}/java -jar ${APP_DIR}/oci-mp-server.jar &> ${LOG_DIR}/oci-mp-server.log'

[Install]
WantedBy=multi-user.target
EOF_INNER

# Set appropriate SELinux Context for helidon-app.service if SELinux is in enforcing mode
SELINUX_ENFORCE_STATUS=$(getenforce)
if [ "${SELINUX_ENFORCE_STATUS}" == "Enforcing" ]; then
echo "Setting context for systemd service file"
chcon system_u:object_r:systemd_unit_file_t:s0 helidon-app.service.new
fi

# These next steps will set and start the application up as a systemd service that will automatically be restarted
# whenever the instance restarts.
if ! systemctl is-enabled helidon-app.service; then
echo "Enabling helidon-app.service"
mv -f helidon-app.service.new helidon-app.service
sudo systemctl enable /home/opc/helidon-app.service
else
# reload systemd manager configuration as helidon-app.service has changed
if ! diff helidon-app.service helidon-app.service.new; then
echo "Reloading systemd manager configuration"
mv -f helidon-app.service.new helidon-app.service
sudo systemctl daemon-reload
# ignore the new helidon-app.service as it has not changed
else
rm -f helidon-app.service.new
fi
fi
echo "Starting helidon-app.service"
sudo systemctl restart helidon-app.service

# Check if Helidon is ready in 60 seconds using the readiness healthcheck endpoint of the app.
TIMEOUT_SEC=60
start_time="$(date -u +%s)"
while true; do
curl -s http://localhost:8080/health/ready | grep -q '"status":"UP"'
if [ $? -eq 0 ]; then
echo "Helidon app is now running with pid $(ps -fe | grep oci-mp-server | grep -v -e bash -e grep | awk '{print $2}')"
break
fi
current_time="$(date -u +%s)"
elapsed_seconds=$(($current_time-$start_time))
if [ $elapsed_seconds -gt $TIMEOUT_SEC ]; then
echo "Error: Helidon app failed to run successfully. Printing the logs..."
cat oci-mp-server.log
exit 1
fi
sleep 1
done
EOF

# delete private.key and application zip
rm -f private.key
rm -f "${HELIDON_MP_APP_ZIP}"
110 changes: 110 additions & 0 deletions hols/oci-basic-setup/get.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
#!/bin/bash
#
# Copyright (c) 2025 Oracle and/or its affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

SCRIPT_DIR=$(dirname "$0")
export TERRAFORM_TFSTATE=${SCRIPT_DIR}/terraform.tfstate
# shellcheck disable=SC1091
source "${SCRIPT_DIR}"/get_common.sh

# Command Choices:
COMPARTMENT_ID_COMMAND=compartment_id
COMPARTMENT_NAME_COMMAND=compartment_name
CUSTOM_LOG_ID_COMMAND=custom_log_id
PUBLIC_IP_COMMAND=public_ip
ALL_COMMAND=all
CREATE_SSH_PRIVATE_KEY_COMMAND=create_ssh_private_key

get_compartment_id() {
get_resource_value compartment_id
echo
}

get_compartment_name() {
get_resource_value compartment_name
echo
}

get_custom_log_id() {
get_resource_value application_log_id
echo
}

get_public_ip() {
get_resource_value instance_public_ip
echo
}

# Extract ssh private key value from output, save in private.key and change file mode to read-only
create_ssh_private_key() {
rm -rf private.key
local private_key
private_key=$(jq -r '.outputs.instance_ssh_private_key.value' "${TERRAFORM_TFSTATE}")
if [[ -n "${private_key}" && "${private_key}" != "null" ]]; then
echo -n "${private_key}" > private.key
chmod go-rw private.key
echo -n "Created private.key and can be used to ssh to the deployment instance by running this command: \"ssh -i private.key opc@"
get_resource_value instance_public_ip
echo "\""
else
echo "Private key does not exist"
fi
}

# Display usage information for this tool.
display_help()
{
local left_justified_size=24
echo "Usage: $(basename "$0") {${COMPARTMENT_ID_COMMAND}|{${COMPARTMENT_NAME_COMMAND}|${CUSTOM_LOG_ID_COMMAND}|${PUBLIC_IP_COMMAND}|${ALL_COMMAND}|${CREATE_SSH_PRIVATE_KEY_COMMAND}}"
echo
print_command_detail ${COMPARTMENT_ID_COMMAND} "displays compartment id" ${left_justified_size}
print_command_detail ${COMPARTMENT_NAME_COMMAND} "displays compartment name" ${left_justified_size}
print_command_detail ${CUSTOM_LOG_ID_COMMAND} "displays application custom log id" ${left_justified_size}
print_command_detail ${PUBLIC_IP_COMMAND} "displays the public ip of the compute host instance used for deployment" ${left_justified_size}
print_command_detail ${ALL_COMMAND} "displays ${COMPARTMENT_ID_COMMAND}, ${CUSTOM_LOG_ID_COMMAND}, ${PUBLIC_IP_COMMAND}" ${left_justified_size}
echo " ---"
print_command_detail ${CREATE_SSH_PRIVATE_KEY_COMMAND} "creates private.key that can be used to ssh to the compute instance" ${left_justified_size}
echo
}

# Main routine
case "${1}" in
"${COMPARTMENT_ID_COMMAND}")
get_compartment_id
;;
"${COMPARTMENT_NAME_COMMAND}")
get_compartment_name
;;
"${CUSTOM_LOG_ID_COMMAND}")
get_custom_log_id
;;
"${PUBLIC_IP_COMMAND}")
get_public_ip
;;
"${CREATE_SSH_PRIVATE_KEY_COMMAND}")
create_ssh_private_key
;;
"${ALL_COMMAND}")
left_justified_size=19
print_resource ${COMPARTMENT_ID_COMMAND} "$(get_compartment_id)" ${left_justified_size}
print_resource ${COMPARTMENT_NAME_COMMAND} "$(get_compartment_name)" ${left_justified_size}
print_resource ${CUSTOM_LOG_ID_COMMAND} "$(get_custom_log_id)" ${left_justified_size}
print_resource ${PUBLIC_IP_COMMAND} "$(get_public_ip)" ${left_justified_size}
;;
*)
display_help
;;
esac
58 changes: 58 additions & 0 deletions hols/oci-basic-setup/get_common.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/bin/bash
#
# Copyright (c) 2025 Oracle and/or its affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

# Extracts resource value from terraform state
get_resource_value() {
parse_tf_output "${1}"
}

# Parses resource from terraform state output
parse_tf_output() {
local resource
resource=$(jq -r '.outputs.'"${1}"'.value' "${TERRAFORM_TFSTATE}")
evaluate_parsed_resource "${resource}"
}

# Evaluate if parsed resource is empty or not
evaluate_parsed_resource() {
if [[ -n "${1}" && "${1}" != "null" ]]; then
echo -n "${1}"
else
echo -n "Requested oci resource does not exist"
fi
}

print_command_detail() {
local key=${1}
local description=${2}
local key_left_justified_size=${3}
printf ' %-'"${key_left_justified_size}"'s' "${key}"
echo "${description}"
}

print_resource() {
local key=${1}
local description=${2}
local key_left_justified_size=${3}
printf '%-'"${key_left_justified_size}"'s: ' "${key}"
echo "${description}"
}

if ! test -f "${TERRAFORM_TFSTATE}"; then
echo "Error: Terraform state (\"${TERRAFORM_TFSTATE}\") does not exist which means the oci resource(s) have not been provisioned yet"
exit 1
fi
6 changes: 6 additions & 0 deletions hols/oci-basic-setup/instance/cloud_init
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#cloud-config
# Open port 8080 for the Helidon application
runcmd:
- echo "Begin firewall port 8080 update" > /var/log/firewall-update.log
- firewall-offline-cmd --add-port=8080/tcp &>> /var/log/firewall-update.log
- systemctl restart firewalld &>> /var/log/firewall-update.log
51 changes: 51 additions & 0 deletions hols/oci-basic-setup/instance/compute.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#
# Copyright (c) 2025 Oracle and/or its affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

# Provisions a compute instance that will be used as the deployment target
resource "oci_core_instance" "compute_instance" {
availability_domain = var.availablity_domain_name == "" ? data.oci_identity_availability_domains.ads.availability_domains[0]["name"] : var.availablity_domain_name
compartment_id = var.compartment_ocid
display_name = "instance${var.resource_name_suffix}"
shape = var.instance_shape
fault_domain = "FAULT-DOMAIN-1"

shape_config {
ocpus = var.instance_ocpus
memory_in_gbs = var.instance_shape_config_memory_in_gbs
}

metadata = {
ssh_authorized_keys = var.ssh_public_key == "" ? tls_private_key.public_private_key_pair.public_key_openssh : var.ssh_public_key
user_data = base64encode(file("./instance/cloud_init"))
}

create_vnic_details {
subnet_id = oci_core_subnet.subnet.id
display_name = "primaryvnic${var.resource_name_suffix}"
assign_public_ip = true
assign_private_dns_record = true
}

source_details {
source_type = "image"
source_id = lookup(data.oci_core_images.compute_instance_images.images[0], "id")
boot_volume_size_in_gbs = "50"
}

timeouts {
create = "60m"
}
}
Loading
Loading