mirror of
https://github.com/super-linter/super-linter.git
synced 2024-11-25 15:41:02 -05:00
629 lines
20 KiB
Bash
Executable file
629 lines
20 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
|
|
################################################################################
|
|
############# Deploy Container to DockerHub @admiralawkbar #####################
|
|
################################################################################
|
|
|
|
# NOTES: This script is used to upload a Dockerfile to DockerHub
|
|
# under the GitHub organization
|
|
# Its based on being built from a GitHub Action, but could be easily updated
|
|
# To be ran in a different medium.
|
|
#
|
|
# PRE-Requirements:
|
|
# - Dockerfile
|
|
# - System with Docker installed
|
|
# - Global variables met
|
|
|
|
###########
|
|
# Globals #
|
|
###########
|
|
GITHUB_WORKSPACE="${GITHUB_WORKSPACE}" # GitHub Workspace
|
|
GITHUB_REPOSITORY="${GITHUB_REPOSITORY}" # GitHub Org/Repo passed from system
|
|
DOCKER_USERNAME="${DOCKER_USERNAME}" # Username to login to DockerHub
|
|
DOCKER_PASSWORD="${DOCKER_PASSWORD}" # Password to login to DockerHub
|
|
GCR_USERNAME="${GCR_USERNAME}" # Username to login to GitHub package registry
|
|
GCR_TOKEN="${GCR_TOKEN}" # Password to login to GitHub package registry
|
|
REGISTRY="${REGISTRY}" # What registry to upload | <GCR> or <Docker>
|
|
IMAGE_REPO="${IMAGE_REPO}" # Image repo to upload the image
|
|
IMAGE_VERSION="${IMAGE_VERSION}" # Version to tag the image
|
|
DOCKERFILE_PATH="${DOCKERFILE_PATH}" # Path to the Dockerfile to be uploaded
|
|
MAJOR_TAG='' # Major tag version if we need to update it
|
|
UPDATE_MAJOR_TAG=0 # Flag to deploy the major tag version as well
|
|
GCR_URL='containers.pkg.github.com' # URL to Github Container Registry
|
|
DOCKER_IMAGE_REPO='' # Docker tag for the image when created
|
|
GCR_IMAGE_REPO='' # Docker tag for the image when created
|
|
FOUND_IMAGE=0 # Flag for if the image has already been built
|
|
CONTAINER_URL='' # Final URL to upload
|
|
|
|
#####################
|
|
# Get the repo name #
|
|
#####################
|
|
REPO_NAME=$(echo "${GITHUB_REPOSITORY}" |cut -f2 -d'/') # GitHub Repository name
|
|
|
|
#########################
|
|
# Source Function Files #
|
|
#########################
|
|
# shellcheck source=/dev/null
|
|
source "${GITHUB_WORKSPACE}/lib/log.sh" # Source the function script(s)
|
|
|
|
################################################################################
|
|
############################ FUNCTIONS BELOW ###################################
|
|
################################################################################
|
|
################################################################################
|
|
#### Function Header ###########################################################
|
|
Header() {
|
|
info "-------------------------------------------------------"
|
|
info "---- GitHub Actions Upload image to [${REGISTRY}] ----"
|
|
info "-------------------------------------------------------"
|
|
}
|
|
################################################################################
|
|
#### Function ValidateInput ####################################################
|
|
ValidateInput() {
|
|
# Need to validate we have the basic variables
|
|
################
|
|
# Print header #
|
|
################
|
|
info "----------------------------------------------"
|
|
info "Gathering variables..."
|
|
info "----------------------------------------------"
|
|
|
|
#############################
|
|
# Validate GITHUB_WORKSPACE #
|
|
#############################
|
|
if [ -z "${GITHUB_WORKSPACE}" ]; then
|
|
error "Failed to get [GITHUB_WORKSPACE]!"
|
|
fatal "[${GITHUB_WORKSPACE}]"
|
|
else
|
|
info "Successfully found:${F[W]}[GITHUB_WORKSPACE]${F[B]}, value:${F[W]}[${GITHUB_WORKSPACE}]"
|
|
fi
|
|
|
|
#####################
|
|
# Validate REGISTRY #
|
|
#####################
|
|
if [ -z "${REGISTRY}" ]; then
|
|
error "Failed to get [REGISTRY]!"
|
|
fatal "[${REGISTRY}]"
|
|
else
|
|
info "Successfully found:${F[W]}[REGISTRY]${F[B]}, value:${F[W]}[${REGISTRY}]"
|
|
fi
|
|
|
|
#####################################################
|
|
# See if we need values for GitHub package Registry #
|
|
#####################################################
|
|
if [[ ${REGISTRY} == "GCR" ]]; then
|
|
#########################
|
|
# Validate GCR_USERNAME #
|
|
#########################
|
|
if [ -z "${GCR_USERNAME}" ]; then
|
|
error "Failed to get [GCR_USERNAME]!"
|
|
fatal "[${GCR_USERNAME}]"
|
|
else
|
|
info "Successfully found:${F[W]}[GCR_USERNAME]${F[B]}, value:${F[W]}[${GCR_USERNAME}]"
|
|
fi
|
|
|
|
######################
|
|
# Validate GCR_TOKEN #
|
|
######################
|
|
if [ -z "${GCR_TOKEN}" ]; then
|
|
error "Failed to get [GCR_TOKEN]!"
|
|
fatal "[${GCR_TOKEN}]"
|
|
else
|
|
info "Successfully found:${F[W]}[GCR_TOKEN]${F[B]}, value:${F[W]}[********]"
|
|
fi
|
|
########################################
|
|
# See if we need values for Ducker hub #
|
|
########################################
|
|
elif [[ ${REGISTRY} == "Docker" ]]; then
|
|
############################
|
|
# Validate DOCKER_USERNAME #
|
|
############################
|
|
if [ -z "${DOCKER_USERNAME}" ]; then
|
|
error "Failed to get [DOCKER_USERNAME]!"
|
|
fatal "[${DOCKER_USERNAME}]"
|
|
else
|
|
info "Successfully found:${F[W]}[DOCKER_USERNAME]${F[B]}, value:${F[W]}[${DOCKER_USERNAME}]"
|
|
fi
|
|
|
|
############################
|
|
# Validate DOCKER_PASSWORD #
|
|
############################
|
|
if [ -z "${DOCKER_PASSWORD}" ]; then
|
|
error "Failed to get [DOCKER_PASSWORD]!"
|
|
fatal "[${DOCKER_PASSWORD}]"
|
|
else
|
|
info "Successfully found:${F[W]}[DOCKER_PASSWORD]${F[B]}, value:${F[B]}[********]"
|
|
fi
|
|
###########################################
|
|
# We were not passed a registry to update #
|
|
###########################################
|
|
else
|
|
error "Failed to find a valid registry!"
|
|
fatal "Registry:[${REGISTRY}]"
|
|
fi
|
|
|
|
#######################
|
|
# Validate IMAGE_REPO #
|
|
#######################
|
|
if [ -z "${IMAGE_REPO}" ]; then
|
|
error "Failed to get [IMAGE_REPO]!"
|
|
fatal "[${IMAGE_REPO}]"
|
|
else
|
|
info "Successfully found:${F[W]}[IMAGE_REPO]${F[B]}, value:${F[W]}[${IMAGE_REPO}]"
|
|
# Set the docker Image repo and GCR image repo
|
|
DOCKER_IMAGE_REPO="${IMAGE_REPO}"
|
|
GCR_IMAGE_REPO="${GCR_URL}/${IMAGE_REPO}/${REPO_NAME}"
|
|
#########################
|
|
# Set the container URL #
|
|
#########################
|
|
if [[ ${REGISTRY} == "Docker" ]]; then
|
|
CONTAINER_URL="${DOCKER_IMAGE_REPO}"
|
|
elif [[ ${REGISTRY} == "GCR" ]]; then
|
|
CONTAINER_URL="${GCR_IMAGE_REPO}"
|
|
fi
|
|
fi
|
|
|
|
##########################
|
|
# Validate IMAGE_VERSION #
|
|
##########################
|
|
if [ -z "${IMAGE_VERSION}" ]; then
|
|
warn "Failed to get [IMAGE_VERSION]!"
|
|
info "Pulling from Branch Name..."
|
|
##############################
|
|
# Get the name of the branch #
|
|
##############################
|
|
BRANCH_NAME=$(git -C "${GITHUB_WORKSPACE}" branch --contains "${GITHUB_SHA}" | awk '{print ${2}}' 2>&1)
|
|
|
|
#######################
|
|
# Load the error code #
|
|
#######################
|
|
ERROR_CODE=$?
|
|
|
|
##############################
|
|
# Check the shell for errors #
|
|
##############################
|
|
if [ ${ERROR_CODE} -ne 0 ]; then
|
|
error "Failed to get branch name!"
|
|
fatal "[${BRANCH_NAME}]"
|
|
fi
|
|
|
|
##################################
|
|
# Remove non alpha-numeric chars #
|
|
##################################
|
|
BRANCH_NAME=$(echo "${BRANCH_NAME}" | tr -cd '[:alnum:]')
|
|
|
|
############################################
|
|
# Set the IMAGE_VERSION to the BRANCH_NAME #
|
|
############################################
|
|
IMAGE_VERSION="${BRANCH_NAME}"
|
|
info "Tag:[${IMAGE_VERSION}]"
|
|
else
|
|
info "Successfully found:${F[W]}[IMAGE_VERSION]${F[B]}, value:${F[W]}[${IMAGE_VERSION}]"
|
|
fi
|
|
|
|
##################################
|
|
# Set regex for getting tag info #
|
|
##################################
|
|
REGEX='(v[0-9]+\.[0-9]+\.[0-9]+)' # Matches 'v1.2.3'
|
|
|
|
######################################################################
|
|
# Check if this is a latest to a versioned release at create new tag #
|
|
######################################################################
|
|
if [[ ${IMAGE_VERSION} =~ ${REGEX} ]]; then
|
|
# Need to get the major version, and set flag to update
|
|
|
|
#####################
|
|
# Set the major tag #
|
|
#####################
|
|
MAJOR_TAG=$(echo "${IMAGE_VERSION}" | cut -d '.' -f1)
|
|
|
|
###################################
|
|
# Set flag for updating major tag #
|
|
###################################
|
|
UPDATE_MAJOR_TAG=1
|
|
|
|
info "- Also deploying a major tag of:[${MAJOR_TAG}]"
|
|
fi
|
|
|
|
############################
|
|
# Validate DOCKERFILE_PATH #
|
|
############################
|
|
if [ -z "${DOCKERFILE_PATH}" ]; then
|
|
error "Failed to get [DOCKERFILE_PATH]!"
|
|
fatal "[${DOCKERFILE_PATH}]"
|
|
else
|
|
info "Successfully found:${F[W]}[DOCKERFILE_PATH]${F[B]}, value:${F[W]}[${DOCKERFILE_PATH}]"
|
|
fi
|
|
}
|
|
################################################################################
|
|
#### Function Authenticate #####################################################
|
|
Authenticate() {
|
|
################
|
|
# Pull in Vars #
|
|
################
|
|
USERNAME="${1}" # Name to auth with
|
|
PASSWORD="${2}" # Password to auth with
|
|
URL="${3}" # Url to auth towards
|
|
NAME="${4}" # name of the service
|
|
|
|
################
|
|
# Print header #
|
|
################
|
|
info "----------------------------------------------"
|
|
info "Login to ${NAME}..."
|
|
info "----------------------------------------------"
|
|
|
|
###################
|
|
# Auth to service #
|
|
###################
|
|
LOGIN_CMD=$(docker login "${URL}" --username "${USERNAME}" --password "${PASSWORD}" 2>&1)
|
|
|
|
#######################
|
|
# Load the error code #
|
|
#######################
|
|
ERROR_CODE=$?
|
|
|
|
##############################
|
|
# Check the shell for errors #
|
|
##############################
|
|
if [ ${ERROR_CODE} -ne 0 ]; then
|
|
# ERROR
|
|
error "Failed to authenticate to ${NAME}!"
|
|
fatal "[${LOGIN_CMD}]"
|
|
else
|
|
# SUCCESS
|
|
info "Successfully authenticated to ${F[C]}${NAME}${F[B]}!"
|
|
fi
|
|
}
|
|
################################################################################
|
|
#### Function BuildImage #######################################################
|
|
BuildImage() {
|
|
################
|
|
# Print header #
|
|
################
|
|
info "----------------------------------------------"
|
|
info "Building the Dockerfile image..."
|
|
info "----------------------------------------------"
|
|
|
|
################################
|
|
# Validate the DOCKERFILE_PATH #
|
|
################################
|
|
if [ ! -f "${DOCKERFILE_PATH}" ]; then
|
|
# No file found
|
|
error "failed to find Dockerfile at:[${DOCKERFILE_PATH}]"
|
|
error "Please make sure you give full path!"
|
|
fatal "Example:[/configs/Dockerfile] or [Dockerfile] if at root directory"
|
|
fi
|
|
|
|
###################
|
|
# Build the image #
|
|
###################
|
|
docker build --no-cache -t "${CONTAINER_URL}:${IMAGE_VERSION}" -f "${DOCKERFILE_PATH}" . 2>&1
|
|
|
|
#######################
|
|
# Load the error code #
|
|
#######################
|
|
ERROR_CODE=$?
|
|
|
|
##############################
|
|
# Check the shell for errors #
|
|
##############################
|
|
if [ ${ERROR_CODE} -ne 0 ]; then
|
|
# ERROR
|
|
fatal "failed to [build] Dockerfile!"
|
|
else
|
|
# SUCCESS
|
|
info "Successfully Built image!"
|
|
fi
|
|
|
|
########################################################
|
|
# Need to see if we need to tag a major update as well #
|
|
########################################################
|
|
if [ ${UPDATE_MAJOR_TAG} -eq 1 ]; then
|
|
# Tag the image with the major tag as well
|
|
docker build -t "${CONTAINER_URL}:${MAJOR_TAG}" -f "${DOCKERFILE_PATH}" . 2>&1
|
|
|
|
#######################
|
|
# Load the error code #
|
|
#######################
|
|
ERROR_CODE=$?
|
|
|
|
##############################
|
|
# Check the shell for errors #
|
|
##############################
|
|
if [ ${ERROR_CODE} -ne 0 ]; then
|
|
# ERROR
|
|
fatal "failed to [tag] Dockerfile!"
|
|
else
|
|
# SUCCESS
|
|
info "Successfully tagged image!"
|
|
fi
|
|
fi
|
|
|
|
#########################
|
|
# Set var to be updated #
|
|
#########################
|
|
ADDITONAL_URL=''
|
|
|
|
####################################
|
|
# Set the additional container URL #
|
|
####################################
|
|
if [[ ${REGISTRY} == "Docker" ]]; then
|
|
ADDITONAL_URL="${GCR_IMAGE_REPO}"
|
|
elif [[ ${REGISTRY} == "GCR" ]]; then
|
|
ADDITONAL_URL="${DOCKER_IMAGE_REPO}"
|
|
fi
|
|
|
|
###################
|
|
# Build the image #
|
|
###################
|
|
docker build -t "${ADDITONAL_URL}:${IMAGE_VERSION}" -f "${DOCKERFILE_PATH}" . 2>&1
|
|
|
|
#######################
|
|
# Load the error code #
|
|
#######################
|
|
ERROR_CODE=$?
|
|
|
|
##############################
|
|
# Check the shell for errors #
|
|
##############################
|
|
if [ ${ERROR_CODE} -ne 0 ]; then
|
|
# ERROR
|
|
fatal "failed to [tag] Version:[${IMAGE_VERSION}] Additonal location Dockerfile!"
|
|
else
|
|
# SUCCESS
|
|
info "Successfull [tag] Version:[${IMAGE_VERSION}] of additonal image!"
|
|
fi
|
|
|
|
###################
|
|
# Build the image #
|
|
###################
|
|
docker build -t "${ADDITONAL_URL}:${MAJOR_TAG}" -f "${DOCKERFILE_PATH}" . 2>&1
|
|
|
|
#######################
|
|
# Load the error code #
|
|
#######################
|
|
ERROR_CODE=$?
|
|
|
|
##############################
|
|
# Check the shell for errors #
|
|
##############################
|
|
if [ ${ERROR_CODE} -ne 0 ]; then
|
|
# ERROR
|
|
fatal "failed to [tag] Version:[${MAJOR_TAG}]Additonal location Dockerfile!"
|
|
else
|
|
# SUCCESS
|
|
info "Successfull [tag] Version:[${MAJOR_TAG}] of additonal image!"
|
|
fi
|
|
}
|
|
################################################################################
|
|
#### Function UploadImage ######################################################
|
|
UploadImage() {
|
|
################
|
|
# Print header #
|
|
################
|
|
info "----------------------------------------------"
|
|
info "Uploading the DockerFile image to ${REGISTRY}..."
|
|
info "----------------------------------------------"
|
|
|
|
############################################
|
|
# Upload the docker image that was created #
|
|
############################################
|
|
docker push "${CONTAINER_URL}:${IMAGE_VERSION}" 2>&1
|
|
|
|
#######################
|
|
# Load the error code #
|
|
#######################
|
|
ERROR_CODE=$?
|
|
|
|
##############################
|
|
# Check the shell for errors #
|
|
##############################
|
|
if [ ${ERROR_CODE} -ne 0 ]; then
|
|
# ERROR
|
|
fatal "failed to [upload] Dockerfile!"
|
|
else
|
|
# SUCCESS
|
|
info "Successfully Uploaded Docker image:${F[W]}[${IMAGE_VERSION}]${F[B]} to ${F[C]}${REGISTRY}${F[B]}!"
|
|
fi
|
|
|
|
#########################
|
|
# Get Image information #
|
|
#########################
|
|
IFS=$'\n' # Set the delimit to newline
|
|
GET_INFO_CMD=$(docker images | grep "${CONTAINER_URL}" | grep "${IMAGE_VERSION}" 2>&1)
|
|
|
|
#######################
|
|
# Load the error code #
|
|
#######################
|
|
ERROR_CODE=$?
|
|
|
|
##############################
|
|
# Check the shell for errors #
|
|
##############################
|
|
if [ ${ERROR_CODE} -ne 0 ]; then
|
|
# ERROR
|
|
error "Failed to get information about built Image!"
|
|
fatal "[${GET_INFO_CMD}]"
|
|
else
|
|
################
|
|
# Get the data #
|
|
################
|
|
REPO=$(echo "${GET_INFO_CMD}" | awk '{print ${1}}')
|
|
TAG=$(echo "${GET_INFO_CMD}" | awk '{print ${2}}')
|
|
IMAGE_ID=$(echo "${GET_INFO_CMD}" | awk '{print ${3}}')
|
|
SIZE="${GET_INFO_CMD##* }"
|
|
|
|
###################
|
|
# Print the goods #
|
|
###################
|
|
info "----------------------------------------------"
|
|
info "Docker Image Details:"
|
|
info "Repository:[${REPO}]"
|
|
info "Tag:[${TAG}]"
|
|
info "Image_ID:[${IMAGE_ID}]"
|
|
info "Size:[${SIZE}]"
|
|
info "----------------------------------------------"
|
|
fi
|
|
|
|
###############################################################
|
|
# Check if we need to upload the major tagged version as well #
|
|
###############################################################
|
|
if [ ${UPDATE_MAJOR_TAG} -eq 1 ]; then
|
|
############################################
|
|
# Upload the docker image that was created #
|
|
############################################
|
|
docker push "${CONTAINER_URL}:${MAJOR_TAG}" 2>&1
|
|
|
|
#######################
|
|
# Load the error code #
|
|
#######################
|
|
ERROR_CODE=$?
|
|
|
|
##############################
|
|
# Check the shell for errors #
|
|
##############################
|
|
if [ ${ERROR_CODE} -ne 0 ]; then
|
|
# ERROR
|
|
fatal "failed to [upload] MAJOR_TAG:[${MAJOR_TAG}] Dockerfile!"
|
|
else
|
|
# SUCCESS
|
|
info "Successfully Uploaded TAG:${F[W]}[${MAJOR_TAG}]${F[B]} of Docker image to ${F[C]}${REGISTRY}${F[B]}!"
|
|
fi
|
|
fi
|
|
}
|
|
################################################################################
|
|
#### Function FindBuiltImage ###################################################
|
|
FindBuiltImage() {
|
|
# Check the local system to see if an image has already been built
|
|
# if so, we only need to update tags and push
|
|
# Set FOUND_IMAGE=1 when found
|
|
|
|
##############
|
|
# Local vars #
|
|
##############
|
|
CHECK_IMAGE_REPO='' # Repo to look for
|
|
|
|
####################################
|
|
# Set the additional container URL #
|
|
####################################
|
|
if [[ ${REGISTRY} == "GCR" ]]; then
|
|
CHECK_IMAGE_REPO="${GCR_IMAGE_REPO}"
|
|
elif [[ ${REGISTRY} == "Docker" ]]; then
|
|
CHECK_IMAGE_REPO="${DOCKER_IMAGE_REPO}"
|
|
fi
|
|
|
|
#######################################
|
|
# Look for Release image in DockerHub #
|
|
#######################################
|
|
FIND_VERSION_CMD=$(docker images | grep "${CHECK_IMAGE_REPO}" | grep "${IMAGE_VERSION}" 2>&1)
|
|
|
|
#######################
|
|
# Load the error code #
|
|
#######################
|
|
ERROR_CODE=$?
|
|
|
|
##############################
|
|
# Check the shell for errors #
|
|
##############################
|
|
if [ $ERROR_CODE -ne 0 ]; then
|
|
info "Found ${REGISTRY} image:[${CHECK_IMAGE_REPO}:${IMAGE_VERSION}] already built on instance"
|
|
# Increment flag
|
|
FOUND_RELASE=1
|
|
else
|
|
info "Failed to find locally created Docker image:[${CHECK_IMAGE_REPO}]"
|
|
info "${FIND_VERSION_CMD}"
|
|
fi
|
|
|
|
#####################################
|
|
# Look for Major image in DockerHub #
|
|
#####################################
|
|
FIND_MAJOR_CMD=$(docker images | grep "${CHECK_IMAGE_REPO}" | grep "${MAJOR_TAG}" 2>&1)
|
|
|
|
#######################
|
|
# Load the error code #
|
|
#######################
|
|
ERROR_CODE=$?
|
|
|
|
##############################
|
|
# Check the shell for errors #
|
|
##############################
|
|
if [ $ERROR_CODE -ne 0 ]; then
|
|
info "Found ${REGISTRY} image:[${CHECK_IMAGE_REPO}:${MAJOR_TAG}] already built on instance"
|
|
# Increment flag
|
|
FOUND_MAJOR=1
|
|
else
|
|
info "Failed to find locally created Docker image:[${FIND_MAJOR_CMD}]"
|
|
info "${FIND_MAJOR_CMD}"
|
|
fi
|
|
|
|
###############################
|
|
# Check if we found the image #
|
|
###############################
|
|
if [ ${FOUND_MAJOR} -eq 1 ] && [ ${FOUND_RELASE} -eq 1 ]; then
|
|
FOUND_IMAGE=1
|
|
fi
|
|
}
|
|
################################################################################
|
|
#### Function Footer ###########################################################
|
|
Footer() {
|
|
info "-------------------------------------------------------"
|
|
info "The step has completed"
|
|
info "-------------------------------------------------------"
|
|
}
|
|
################################################################################
|
|
################################## MAIN ########################################
|
|
################################################################################
|
|
|
|
##########
|
|
# Header #
|
|
##########
|
|
Header
|
|
|
|
##################
|
|
# Validate Input #
|
|
##################
|
|
ValidateInput
|
|
|
|
###############################
|
|
# Find Image if already built #
|
|
###############################
|
|
FindBuiltImage
|
|
|
|
###################
|
|
# Build the image #
|
|
###################
|
|
if [ "$FOUND_IMAGE" -ne 0 ]; then
|
|
BuildImage
|
|
fi
|
|
|
|
######################
|
|
# Login to DockerHub #
|
|
######################
|
|
if [[ ${REGISTRY} == "Docker" ]]; then
|
|
# Authenticate "Username" "Password" "Url" "Name"
|
|
Authenticate "${DOCKER_USERNAME}" "${DOCKER_PASSWORD}" "" "Dockerhub"
|
|
|
|
######################################
|
|
# Login to GitHub Container Registry #
|
|
######################################
|
|
elif [[ ${REGISTRY} == "GCR" ]]; then
|
|
# Authenticate "Username" "Password" "Url" "Name"
|
|
Authenticate "${GCR_USERNAME}" "${GCR_TOKEN}" "https://${GCR_URL}" "GitHub Container Registry"
|
|
|
|
else
|
|
#########
|
|
# ERROR #
|
|
#########
|
|
error "Registry not set correctly!"
|
|
fatal "Registry:[${REGISTRY}]"
|
|
fi
|
|
|
|
####################
|
|
# Upload the image #
|
|
####################
|
|
UploadImage
|
|
|
|
##########
|
|
# Footer #
|
|
##########
|
|
Footer
|