more docker changes
This commit is contained in:
		
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -42,5 +42,4 @@ api/tacticalrmm/accounts/management/commands/random_data.py
 | 
			
		||||
versioninfo.go
 | 
			
		||||
resource.syso
 | 
			
		||||
htmlcov/
 | 
			
		||||
docker-compose.override.yml
 | 
			
		||||
docker-compose.dev.yml
 | 
			
		||||
@@ -50,26 +50,26 @@ class Command(BaseCommand):
 | 
			
		||||
    def handle(self, *args, **kwargs):
 | 
			
		||||
        self.mesh_settings = CoreSettings.objects.first()
 | 
			
		||||
 | 
			
		||||
        # set mesh token if not set
 | 
			
		||||
        if not self.mesh_settings.mesh_token and not hasattr(
 | 
			
		||||
            settings, "MESH_TOKEN_KEY"
 | 
			
		||||
        ):
 | 
			
		||||
            filepath = "/opt/tactical/tmp/mesh_token"
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
                with open(filepath, "r") as read_file:
 | 
			
		||||
                    key = read_file.readlines()
 | 
			
		||||
        # Check for Mesh Username
 | 
			
		||||
            if not self.mesh_settings.mesh_username and settings.MESH_USERNAME:
 | 
			
		||||
                self.mesh_settings.mesh_username = settings.MESH_USERNAME
 | 
			
		||||
 | 
			
		||||
                    # Remove key file contents for security reasons
 | 
			
		||||
                    with open(filepath, "w") as write_file:
 | 
			
		||||
                        write_file.write("")
 | 
			
		||||
        # Check for Mesh Site
 | 
			
		||||
            if not self.mesh_settings.mesh_site and settings.MESH_SITE:
 | 
			
		||||
                self.mesh_settings.mesh_site = settings.MESH_SITE
 | 
			
		||||
 | 
			
		||||
                    # readlines() returns an array. Get first item
 | 
			
		||||
                    self.mesh_settings.mesh_token = key[0].rstrip()
 | 
			
		||||
                    self.mesh_settings.save()
 | 
			
		||||
            except:
 | 
			
		||||
                self.stdout.write("Mesh Central key wasn't found")
 | 
			
		||||
                return
 | 
			
		||||
        # Check for Mesh Token
 | 
			
		||||
            if not self.mesh_settings.mesh_token and settings.MESH_TOKEN_KEY:
 | 
			
		||||
                self.mesh_settings.mesh_token = settings.MESH_TOKEN_KEY
 | 
			
		||||
 | 
			
		||||
        except AttributeError:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        if self.mesh_settings.mesh_token:
 | 
			
		||||
            asyncio.get_event_loop().run_until_complete(self.websocket_call())
 | 
			
		||||
            self.stdout.write("Initial Mesh Central setup complete")
 | 
			
		||||
        else:
 | 
			
		||||
            self.stdout.write(
 | 
			
		||||
                "Mesh Setup was skipped being the token wasn't set. Set it up manually."
 | 
			
		||||
            )
 | 
			
		||||
 
 | 
			
		||||
@@ -18,7 +18,10 @@ TZ_CHOICES = [(_, _) for _ in pytz.all_timezones]
 | 
			
		||||
 | 
			
		||||
class CoreSettings(BaseAuditModel):
 | 
			
		||||
    email_alert_recipients = ArrayField(
 | 
			
		||||
        models.EmailField(null=True, blank=True), null=True, blank=True, default=list,
 | 
			
		||||
        models.EmailField(null=True, blank=True),
 | 
			
		||||
        null=True,
 | 
			
		||||
        blank=True,
 | 
			
		||||
        default=list,
 | 
			
		||||
    )
 | 
			
		||||
    sms_alert_recipients = ArrayField(
 | 
			
		||||
        models.CharField(max_length=255, null=True, blank=True),
 | 
			
		||||
@@ -69,17 +72,6 @@ class CoreSettings(BaseAuditModel):
 | 
			
		||||
        if not self.pk and CoreSettings.objects.exists():
 | 
			
		||||
            raise ValidationError("There can only be one CoreSettings instance")
 | 
			
		||||
 | 
			
		||||
        # Only runs on first create
 | 
			
		||||
        if not self.pk:
 | 
			
		||||
            mesh_settings = self.get_initial_mesh_settings()
 | 
			
		||||
 | 
			
		||||
            if "mesh_token" in mesh_settings:
 | 
			
		||||
                self.mesh_token = mesh_settings["mesh_token"]
 | 
			
		||||
            if "mesh_username" in mesh_settings:
 | 
			
		||||
                self.mesh_username = mesh_settings["mesh_username"]
 | 
			
		||||
            if "mesh_site" in mesh_settings:
 | 
			
		||||
                self.mesh_site = mesh_settings["mesh_site"]
 | 
			
		||||
 | 
			
		||||
        return super(CoreSettings, self).save(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
@@ -165,39 +157,6 @@ class CoreSettings(BaseAuditModel):
 | 
			
		||||
            except Exception as e:
 | 
			
		||||
                logger.error(f"SMS failed to send: {e}")
 | 
			
		||||
 | 
			
		||||
    def get_initial_mesh_settings(self):
 | 
			
		||||
 | 
			
		||||
        mesh_settings = {}
 | 
			
		||||
 | 
			
		||||
        # Check for Mesh Username
 | 
			
		||||
        try:
 | 
			
		||||
            if settings.MESH_USERNAME:
 | 
			
		||||
                mesh_settings["mesh_username"] = settings.MESH_USERNAME
 | 
			
		||||
            else:
 | 
			
		||||
                raise AttributeError("MESH_USERNAME doesn't exist")
 | 
			
		||||
        except AttributeError:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        # Check for Mesh Site
 | 
			
		||||
        try:
 | 
			
		||||
            if settings.MESH_SITE:
 | 
			
		||||
                mesh_settings["mesh_site"] = settings.MESH_SITE
 | 
			
		||||
            else:
 | 
			
		||||
                raise AttributeError("MESH_SITE doesn't exist")
 | 
			
		||||
        except AttributeError:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        # Check for Mesh Token
 | 
			
		||||
        try:
 | 
			
		||||
            if settings.MESH_TOKEN_KEY:
 | 
			
		||||
                mesh_settings["mesh_token"] = settings.MESH_TOKEN_KEY
 | 
			
		||||
            else:
 | 
			
		||||
                raise AttributeError("MESH_SITE doesn't exist")
 | 
			
		||||
        except AttributeError:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        return mesh_settings
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def serialize(core):
 | 
			
		||||
        # serializes the core and returns json
 | 
			
		||||
 
 | 
			
		||||
@@ -1,24 +1,24 @@
 | 
			
		||||
MESH_HOST=mesh.example.com
 | 
			
		||||
MESH_USER=mesh
 | 
			
		||||
MESH_PASS=meshpass
 | 
			
		||||
EMAIL_USER=admin@example.com
 | 
			
		||||
IMAGE_REPO=
 | 
			
		||||
VERSION=latest
 | 
			
		||||
 | 
			
		||||
# tactical credentials (Used to login to dashboard)
 | 
			
		||||
TRMM_USER=tactical
 | 
			
		||||
TRMM_PASS=tactical
 | 
			
		||||
 | 
			
		||||
# dns settings
 | 
			
		||||
APP_HOST=app.simplermm.com
 | 
			
		||||
API_HOST=api.simplermm.com
 | 
			
		||||
MESH_HOST=mesh.simplermm.com
 | 
			
		||||
 | 
			
		||||
# mesh settings
 | 
			
		||||
MESH_USER=meshcentral
 | 
			
		||||
MESH_PASS=meshcentralpass
 | 
			
		||||
MONGODB_USER=mongouser
 | 
			
		||||
MONGODB_PASSWORD=mongopass
 | 
			
		||||
 | 
			
		||||
# database settings
 | 
			
		||||
POSTGRES_USER=postgres
 | 
			
		||||
POSTGRES_PASS=pass
 | 
			
		||||
POSTGRES_HOST=db
 | 
			
		||||
POSTGRES_PASS=postgrespass
 | 
			
		||||
 | 
			
		||||
APP_HOST=app.example.com
 | 
			
		||||
API_HOST=api.example.com
 | 
			
		||||
 | 
			
		||||
REDIS_HOST=redis
 | 
			
		||||
 | 
			
		||||
SALT_HOST=salt
 | 
			
		||||
SALT_USER=saltapi
 | 
			
		||||
SALT_PASS=password
 | 
			
		||||
 | 
			
		||||
ADMIN_URL=admin
 | 
			
		||||
DJANGO_SEKRET=secret12341234123412341234
 | 
			
		||||
DJANGO_DEBUG=False
 | 
			
		||||
# salt settings
 | 
			
		||||
SALT_PASS=saltpass
 | 
			
		||||
@@ -1,5 +1,7 @@
 | 
			
		||||
FROM ubuntu:20.04
 | 
			
		||||
 | 
			
		||||
ENV TACTICAL_DIR /opt/tactical
 | 
			
		||||
 | 
			
		||||
RUN apt-get update && \
 | 
			
		||||
    apt-get install -y ca-certificates wget gnupg2 tzdata supervisor && \
 | 
			
		||||
    wget -O - https://repo.saltstack.com/py3/ubuntu/20.04/amd64/latest/SALTSTACK-GPG-KEY.pub | apt-key add - && \
 | 
			
		||||
 
 | 
			
		||||
@@ -5,11 +5,11 @@ set -e
 | 
			
		||||
: "${SALT_USER:=saltapi}"
 | 
			
		||||
: "${SALT_USER:=saltpass}"
 | 
			
		||||
 | 
			
		||||
# create salt user
 | 
			
		||||
useradd -M -s /bin/bash -u 1000 "${SALT_USER}"
 | 
			
		||||
# create sal user
 | 
			
		||||
groupadd -g 1000 "${SALT_USER}" 
 | 
			
		||||
useradd -M -d "/opt/tactical" -s /bin/bash -u 1000 -g 1000 "${SALT_USER}"
 | 
			
		||||
echo "${SALT_USER}:${SALT_PASS}" | chpasswd
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
cherrypy_config="$(cat << EOF
 | 
			
		||||
module_dirs: ["/opt/tactical/_modules"]
 | 
			
		||||
timeout: 20
 | 
			
		||||
 
 | 
			
		||||
@@ -4,50 +4,35 @@ ARG DEBIAN_FRONTEND=noninteractive
 | 
			
		||||
ARG BUILD_DATE
 | 
			
		||||
 | 
			
		||||
ENV TACTICAL_DIR /opt/tactical
 | 
			
		||||
ENV TACTICAL_TMP_DIR /tmp/tacticalrmm/
 | 
			
		||||
ENV TACTICAL_GO_DIR /usr/local/rmmgo
 | 
			
		||||
ENV TACTICAL_READY_FILE ${TACTICAL_DIR}/tmp/tactical.ready
 | 
			
		||||
ENV TACTICAL_USER tactical
 | 
			
		||||
ENV PATH ${TACTICAL_DIR}/api/env/bin:$PATH
 | 
			
		||||
 | 
			
		||||
SHELL ["/bin/bash", "-e", "-o", "pipefail", "-c"]
 | 
			
		||||
 | 
			
		||||
# install tactical reqs
 | 
			
		||||
COPY api/tacticalrmm/requirements.txt ${TACTICAL_TMP_DIR}
 | 
			
		||||
# copy files from repo
 | 
			
		||||
COPY api/tacticalrmm ${TACTICAL_DIR}/api
 | 
			
		||||
COPY scripts ${TACTICAL_DIR}/scripts
 | 
			
		||||
COPY _modules ${TACTICAL_DIR}/_modules
 | 
			
		||||
COPY api/tacticalrmm/core/goinstaller/bin/goversioninfo /usr/local/bin/goversioninfo
 | 
			
		||||
 | 
			
		||||
# install deps
 | 
			
		||||
RUN apt-get update && \
 | 
			
		||||
    apt-get upgrade -y && \
 | 
			
		||||
    apt-get install -y --no-install-recommends wget ca-certificates gcc libc6-dev && \
 | 
			
		||||
    rm -rf /var/lib/apt/lists/* && \
 | 
			
		||||
    pip install --upgrade pip && \
 | 
			
		||||
    mkdir -p ${TACTICAL_DIR}/api && \
 | 
			
		||||
    pip install --no-cache-dir virtualenv && python -m virtualenv ${TACTICAL_DIR}/api/env && \
 | 
			
		||||
    ${TACTICAL_DIR}/api/env/bin/pip install --no-cache-dir setuptools wheel gunicorn && \
 | 
			
		||||
    ${TACTICAL_DIR}/api/env/bin/pip install --no-cache-dir -r ${TACTICAL_TMP_DIR}/requirements.txt && \
 | 
			
		||||
    ${TACTICAL_DIR}/api/env/bin/pip install --no-cache-dir -r ${TACTICAL_DIR}/api/requirements.txt && \
 | 
			
		||||
    wget https://golang.org/dl/go1.15.linux-amd64.tar.gz -P /tmp && \
 | 
			
		||||
    tar -xzf /tmp/go1.15.linux-amd64.tar.gz -C /tmp
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
FROM python:3.8-slim
 | 
			
		||||
 | 
			
		||||
ARG DEBIAN_FRONTEND=noninteractive
 | 
			
		||||
ARG BUILD_DATE
 | 
			
		||||
 | 
			
		||||
ENV TACTICAL_DIR /opt/tactical
 | 
			
		||||
ENV TACTICAL_TMP_DIR /tmp/tacticalrmm
 | 
			
		||||
ENV TACTICAL_GO_DIR /usr/local/rmmgo
 | 
			
		||||
ENV TACTICAL_READY_FILE ${TACTICAL_DIR}/tmp/tactical.ready
 | 
			
		||||
ENV TACTICAL_USER tactical
 | 
			
		||||
 | 
			
		||||
# create tactical user
 | 
			
		||||
RUN groupadd -g 1000 "${TACTICAL_USER}" && \
 | 
			
		||||
    mkdir -p ${TACTICAL_GO_DIR}/go && \
 | 
			
		||||
    tar -xzf /tmp/go1.15.linux-amd64.tar.gz -C ${TACTICAL_GO_DIR}/go && \
 | 
			
		||||
    rm -f /tmp/go1.15.linux-amd64.tar.gz && \
 | 
			
		||||
    groupadd -g 1000 "${TACTICAL_USER}" && \
 | 
			
		||||
    useradd -M -d "${TACTICAL_DIR}" -s /bin/bash -u 1000 -g 1000 "${TACTICAL_USER}"
 | 
			
		||||
 | 
			
		||||
# copy files from repo and 1st build step
 | 
			
		||||
COPY api/tacticalrmm ${TACTICAL_DIR}/api
 | 
			
		||||
COPY scripts ${TACTICAL_DIR}/scripts
 | 
			
		||||
COPY _modules ${TACTICAL_DIR}/_modules
 | 
			
		||||
COPY api/tacticalrmm/core/goinstaller/bin/goversioninfo /usr/local/bin/goversioninfo
 | 
			
		||||
COPY --from=builder /tmp/go ${TACTICAL_GO_DIR}/go
 | 
			
		||||
 | 
			
		||||
# docker init
 | 
			
		||||
COPY docker/containers/tactical/entrypoint.sh /
 | 
			
		||||
RUN chmod +x /entrypoint.sh
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,8 @@
 | 
			
		||||
 | 
			
		||||
set -e
 | 
			
		||||
 | 
			
		||||
: "${TRMM_USER:=tactical}"
 | 
			
		||||
: "${TRMM_PASS:=tactical}"
 | 
			
		||||
: "${POSTGRES_HOST:=tactical-postgres}"
 | 
			
		||||
: "${POSTGRES_PORT:=5432}"
 | 
			
		||||
: "${POSTGRES_USER:=tactical}"
 | 
			
		||||
@@ -19,8 +21,6 @@ set -e
 | 
			
		||||
: "${REDIS_HOST:=tactical-redis}"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
source ${TACTICAL_DIR}/api/env/bin/activate
 | 
			
		||||
 | 
			
		||||
function check_tactical_ready {
 | 
			
		||||
  sleep 15
 | 
			
		||||
  until [ -f "${TACTICAL_READY_FILE}" ]; do
 | 
			
		||||
@@ -42,7 +42,14 @@ if [ "$1" = 'tactical-init' ]; then
 | 
			
		||||
    sleep 5
 | 
			
		||||
  done
 | 
			
		||||
 | 
			
		||||
  # check mesh setup and wait for mesh token
 | 
			
		||||
  until [ -f "${TACTICAL_DIR}/tmp/mesh_token" ]; do
 | 
			
		||||
    echo "waiting for mesh token to be generated..."
 | 
			
		||||
    sleep 10
 | 
			
		||||
  done
 | 
			
		||||
 | 
			
		||||
  # configure django settings
 | 
			
		||||
  MESH_TOKEN=$(cat ${TACTICAL_DIR}/tmp/mesh_token)
 | 
			
		||||
  DJANGO_SEKRET=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 80 | head -n 1)
 | 
			
		||||
  SALT_PASS=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 20 | head -n 1)
 | 
			
		||||
  ADMINURL=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 70 | head -n 1)
 | 
			
		||||
@@ -96,6 +103,7 @@ SALT_PASSWORD = '${SALT_PASS}'
 | 
			
		||||
SALT_HOST     = '${SALT_HOST}'
 | 
			
		||||
MESH_USERNAME = '${MESH_USER}'
 | 
			
		||||
MESH_SITE = 'https://${MESH_HOST}'
 | 
			
		||||
MESH_TOKEN_KEY = '${MESH_TOKEN_KEY}'
 | 
			
		||||
REDIS_HOST    = '${REDIS_HOST}'
 | 
			
		||||
MESH_WS_URL = 'ws://${MESH_CONTAINER}:443'
 | 
			
		||||
EOF
 | 
			
		||||
@@ -103,12 +111,6 @@ EOF
 | 
			
		||||
 | 
			
		||||
  echo "${localvars}" > ${TACTICAL_DIR}/api/tacticalrmm/local_settings.py
 | 
			
		||||
 | 
			
		||||
  # check mesh setup and wait for mesh token
 | 
			
		||||
  until [ -f "${TACTICAL_DIR}/tmp/mesh_token" ]; do
 | 
			
		||||
    echo "waiting for mesh token to be generated..."
 | 
			
		||||
    sleep 10
 | 
			
		||||
  done
 | 
			
		||||
 | 
			
		||||
  # run migrations and init scripts
 | 
			
		||||
  python manage.py migrate --no-input
 | 
			
		||||
  python manage.py collectstatic --no-input
 | 
			
		||||
@@ -117,6 +119,9 @@ EOF
 | 
			
		||||
  python manage.py load_chocos
 | 
			
		||||
  python manage.py load_community_scripts
 | 
			
		||||
 | 
			
		||||
  # create super user
 | 
			
		||||
  echo "from django.contrib.auth.models import User; User.objects.create_superuser('${TRMM_USER}', 'admin@example.com', '${TRMM_PASS}')" | python manage.py shell
 | 
			
		||||
 | 
			
		||||
  # chown everything to tactical user
 | 
			
		||||
  chown -R "${TACTICAL_USER}":"${TACTICAL_USER}" "${TACTICAL_DIR}"
 | 
			
		||||
 | 
			
		||||
@@ -130,6 +135,7 @@ if [ "$1" = 'tactical-backend' ]; then
 | 
			
		||||
  check_tactical_ready
 | 
			
		||||
 | 
			
		||||
  # Prepare log files and start outputting logs to stdout
 | 
			
		||||
  mkdir -p ${TACTICAL_DIR}/api/tacticalrmm/logs
 | 
			
		||||
  touch ${TACTICAL_DIR}/api/tacticalrmm/logs/gunicorn.log
 | 
			
		||||
  touch ${TACTICAL_DIR}/api/tacticalrmm/logs/gunicorn-access.log
 | 
			
		||||
  tail -n 0 -f ${TACTICAL_DIR}/api/tacticalrmm/logs/gunicorn*.log &
 | 
			
		||||
@@ -147,14 +153,17 @@ if [ "$1" = 'tactical-backend' ]; then
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [ "$1" = 'tactical-celery' ]; then
 | 
			
		||||
  check_tactical_ready
 | 
			
		||||
  test -f "${TACTICAL_DIR}/api/celerybeat.pid" && rm "${TACTICAL_DIR}/api/celerybeat.pid"
 | 
			
		||||
  celery -A tacticalrmm worker
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [ "$1" = 'tactical-beat' ]; then
 | 
			
		||||
  check_tactical_ready
 | 
			
		||||
  celery -A tacticalrmm beat
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [ "$1" = 'tactical-celerywinupdate' ]; then
 | 
			
		||||
  check_tactical_ready
 | 
			
		||||
  celery -A tacticalrmm worker -Q wupdate
 | 
			
		||||
fi
 | 
			
		||||
@@ -15,7 +15,6 @@ networks:
 | 
			
		||||
# docker managed persistent volumes
 | 
			
		||||
volumes:
 | 
			
		||||
  tactical_data:
 | 
			
		||||
  tactical_certs:
 | 
			
		||||
  salt_data:
 | 
			
		||||
  postgres_data:
 | 
			
		||||
  mongo_data:
 | 
			
		||||
@@ -51,6 +50,8 @@ services:
 | 
			
		||||
      APP_HOST: ${APP_HOST}
 | 
			
		||||
      API_HOST: ${API_HOST}
 | 
			
		||||
      MESH_HOST: ${MESH_HOST}
 | 
			
		||||
      TRMM_USER: ${TRMM_USER}
 | 
			
		||||
      TRMM_PASS: ${TRMM_PASS}
 | 
			
		||||
    depends_on:
 | 
			
		||||
      - tactical-postgres
 | 
			
		||||
      - tactical-meshcentral    
 | 
			
		||||
@@ -112,7 +113,7 @@ services:
 | 
			
		||||
    networks:
 | 
			
		||||
      - proxy
 | 
			
		||||
    environment:
 | 
			
		||||
      - API_HOST=${API_HOST}
 | 
			
		||||
      API_HOST: ${API_HOST}
 | 
			
		||||
 | 
			
		||||
  # container for django backend
 | 
			
		||||
  tactical-backend:
 | 
			
		||||
@@ -132,9 +133,9 @@ services:
 | 
			
		||||
    image: ${IMAGE_REPO}tactical-nginx:${VERSION}
 | 
			
		||||
    restart: always
 | 
			
		||||
    environment:
 | 
			
		||||
      - APP_HOST=${APP_HOST}
 | 
			
		||||
      - API_HOST=${API_HOST}
 | 
			
		||||
      - MESH_HOST=${MESH_HOST}
 | 
			
		||||
      APP_HOST: ${APP_HOST}
 | 
			
		||||
      API_HOST: ${API_HOST}
 | 
			
		||||
      MESH_HOST: ${MESH_HOST}
 | 
			
		||||
    networks:
 | 
			
		||||
      proxy:
 | 
			
		||||
        ipv4_address: 172.20.0.20
 | 
			
		||||
 
 | 
			
		||||
@@ -5,13 +5,12 @@
 | 
			
		||||
set -o errexit
 | 
			
		||||
set -o pipefail
 | 
			
		||||
 | 
			
		||||
# tactical tactical-nginx tactical-meshcentral tactical-salt 
 | 
			
		||||
 | 
			
		||||
DOCKER_IMAGES="tactical-frontend"
 | 
			
		||||
#  tactical-frontend tactical-nginx tactical-salt --no-cache
 | 
			
		||||
DOCKER_IMAGES="tactical"
 | 
			
		||||
 | 
			
		||||
cd ..
 | 
			
		||||
 | 
			
		||||
for DOCKER_IMAGE in ${DOCKER_IMAGES}; do
 | 
			
		||||
  echo "Building Tactical Image: ${DOCKER_IMAGE}..."
 | 
			
		||||
  docker build --pull --no-cache --build-arg BUILD_DATE="$(date -u +'%Y-%m-%dT%H:%M:%SZ')" -t "${DOCKER_IMAGE}" -f "docker/containers/${DOCKER_IMAGE}/dockerfile" .
 | 
			
		||||
  docker build --pull --build-arg BUILD_DATE="$(date -u +'%Y-%m-%dT%H:%M:%SZ')" -t "${DOCKER_IMAGE}" -f "docker/containers/${DOCKER_IMAGE}/dockerfile" .
 | 
			
		||||
done
 | 
			
		||||
		Reference in New Issue
	
	Block a user