diff --git a/.gitignore b/.gitignore
index d5ec74aca..b6d155b1c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,3 +37,5 @@ docs/wiki/_templates
cfg/InOut/*.42
cfg/InOut/*.csv
+
+.DS_Store
\ No newline at end of file
diff --git a/deployments/.gitignore b/deployments/.gitignore
new file mode 100644
index 000000000..d2fdb96a5
--- /dev/null
+++ b/deployments/.gitignore
@@ -0,0 +1,5 @@
+environments/*
+.DS_Store
+*.42
+.env
+settings.xml
diff --git a/deployments/Makefile b/deployments/Makefile
new file mode 100644
index 000000000..124e9a9e9
--- /dev/null
+++ b/deployments/Makefile
@@ -0,0 +1,31 @@
+.DEFAULT_GOAL := help
+.ONESHELL:
+
+SHELL=bash
+
+install-taskfile: ## USE TASK instead of Makefile: Install Taskfile CLI tool
+ @sh -c "$$(curl --location https://taskfile.dev/install.sh)" -- -d -b ~/.local/bin && \
+ chmod +x ~/.local/bin/task && export PATH="${HOME}/.local/bin:${PATH}"
+ @echo; echo "add: ${HOME}/.local/bin:${PATH} to your ~/.bashrc or ~/.zshrc"; echo
+
+install-lazydocker: ## Install lazydocker CLI tool (to view logs, etc)
+ @curl --silent https://raw.githubusercontent.com/jesseduffield/lazydocker/master/scripts/install_update_linux.sh | bash
+ @echo; echo "don't forget to add: ${HOME}/.local/bin:${PATH} to your ~/.bashrc or ~/.zshrc"; echo
+ @echo "watch a Lazydocker demo here: https://www.youtube.com/watch?v=NICqQPxwJWw"; echo
+
+RESET = \033[0m
+PURPLE = \033[0;35m
+GREEN = \033[0;32m
+LINE = $(PURPLE)----------------------------------------------------------------------------------------$(RESET)
+
+help:
+ @echo
+ @printf "\033[37m%-30s\033[0m %s\n" "#----------------------------------------------------------------------------------------"
+ @printf "\033[37m%-30s\033[0m %s\n" "# Makefile targets "
+ @printf "\033[37m%-30s\033[0m %s\n" "#----------------------------------------------------------------------------------------"
+ @echo
+ @printf "\033[37m%-30s\033[0m %s\n" "#-target-----------------------description-----------------------------------------------"
+ @grep -E '^[a-zA-Z_-].+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
+ @echo
+
+print-% : ; @echo $* = $($*)
diff --git a/deployments/README.md b/deployments/README.md
new file mode 100644
index 000000000..e010a534f
--- /dev/null
+++ b/deployments/README.md
@@ -0,0 +1,327 @@
+# How to use
+
+## Dependencies
+
+- `gnu make`
+- `bash`
+- `task` a contemporary version of `make`
+- `lazydocker`
+
+Which can be installed via `make install-lazydocker install-taskfile`
+
+## prep - onetime run
+
+```bash
+make install-lazydocker install-taskfile env-create
+
+```
+
+## Run `task` after you've done `make install-taskfile`:
+
+```bash
+task
+
+```
+
+returns:
+
+```bash
+task: Available tasks for this project:
+* build: Build all containers
+* build-fortytwo: Build fortytwo (42)
+* build-fsw: Build nos3-base-local (fsw)
+* build-openmct: Build openmct
+* build-yamcs: Build yamcs
+* check-docker-ps: check docker ps for any exited|unhealthy|created|error containers
+* check-service-ready: wait for a service to be ready on a given port
+* clean: clean nos3 configuration
+* clean-containers: clean images, cache, volumes, and networks
+* config: configure nos3 by running config.sh
+* config-nos3-mission: configure nos3-mission.xml to set start-time
+* default: Shows this help message
+* down: Bring down nos3
+* env-create: Create .env file from env.sh script
+* info-services: info on accessing various services
+* install-lazydocker: Install lazydocker CLI tool (to view logs, etc)
+* purge-containers: WARNING: purge docker/podman images, cache, volumes, and networks
+* set-permissions: set filesystem permissions, needed for restricted accounts
+* setup-maven: Setup maven settings.xml with proxy info-services
+* sidecar: Run sidecar to enable output
+* submodule-update: sync and update git submodules
+* up: Bring up nos3 in detached state
+* up-minimum: Bring up nos3 (minimum) in detached state
+* k8s:apply:deployment: apply nos3 kubernetes deployment(s)
+* k8s:apply:ingress:nginx: apply ingress-nginx for nos3 kubernetes
+* k8s:apply:kustomization: Apply kustomization to the namespace
+* k8s:convert:kompose: convert docker-compose to kubernetes manifests using kompose
+* k8s:convert:kompose:helm: convert docker-compose to kubernetes manifests using kompose
+* k8s:create:namespace: Create the Kubernetes namespace
+* k8s:delete:deployment: delete nos3 kubernetes deployment(s)
+* k8s:delete:kustomization: Delete kustomize and namespace
+* k8s:delete:namespace: delete nos3 kubernetes namespace
+* k8s:generate:all: Generate all kubernetes yamls for COMPONENT_NAME
+* k8s:generate:configmap:args: Generate kubernetes configmaps yaml for COMPONENT_NAME
+* k8s:generate:configmap:env: Generate kubernetes configmaps yaml for COMPONENT_NAME
+* k8s:generate:configmap:volumes: Generate kubernetes volumes configmaps yaml for COMPONENT_NAME
+* k8s:generate:configmaps:all: Generate all kubernetes yamls for COMPONENT_NAME
+* k8s:generate:deployment: Generate kubernetes deployment yaml for COMPONENT_NAME
+* k8s:generate:ingress: Generate kubernetes configmaps yaml for COMPONENT_NAME
+* k8s:generate:kustomization: Generate Kustomization yaml for COMPONENT_NAME
+* k8s:generate:service: Generate kubernetes service yaml for COMPONENT_NAME
+* k8s:get:configmap: get a specific kubernetes configmaps defined in K8S_CONFIGMAP_NAME
+* k8s:get:configmaps: get nos3 kubernetes configmaps
+* k8s:get:pod-name: get the pod name for nos3 kubernetes deployment
+* k8s:kill:port-forward: Kill port-forward
+* k8s:path:yamls: generate kubernetes yamls for the deployment
+* k8s:port-forward: Port forward to access services locally
+* k8s:write:env: write k8s env file
+
+
+```
+
+## Run - which actually also runs `task submodule-update`
+
+```bash
+task up
+
+```
+
+### Taskfile
+
+more info on `task` can be found here https://taskfile.dev
+
+### Lazydocker
+
+more info on `lazydocker` can be found here https://github.com/jesseduffield/lazydocker
+
+
+## Test
+
+open http://localhost:8090/instance?c=nos3__realtime to access yamcs/gsw
+open http://localhost:30090/vnc.html to access fortytwo (42)
+open http://localhost:9000 to access openmct
+
+## Diagrams
+
+Ignore the below for now
+
+```mermaid
+graph TD
+ A[ubuntu:jammy-20250126]-->|FROM| B
+ B[nasa-itc/deployments/] -->|build-->push| C(docker.io/ivvitc/nos3-64:dev)
+ C-->|FROM| D[haisamido/nos3-64/]
+ D-->|build-->push| E(ghcr.io/haisamido/nos3-64:dev)
+
+```
+
+```mermaid
+graph TD;
+ %% client([client])-. Ingress-managed
load balancer .->ingress[Ingress];
+ %% ingress-->|routing rule|service[Service];
+
+ subgraph namespace
+ %% ingress;
+ %% gps-->|tcp:4245|fortytwo[fortytwo]
+ %% time-->message_bus
+
+ rw0-->|rx:4278|fortytwo
+ fortytwo[fortytwo]-->|tx:4277|rw0
+ %% rw0-->|uart:2|uart-bus
+
+ rw1-->|rx:4378|fortytwo
+ fortytwo[fortytwo]-->|tx:4377|rw1
+ %% rw1-->|uart:3|uart-bus
+
+ rw2-->|rx:4478|fortytwo
+ fortytwo[fortytwo]-->|tx:4477|rw2
+ %% rw2-->|uart:4|uart-bus
+
+ torquer-->|rx:4279|fortytwo[fortytwo]
+ thruster-->|rx:4280|fortytwo[fortytwo]
+ %% thruster-->|uart:29|uart-bus
+
+ fortytwo[fortytwo]-->|tx:4245|gps
+ gps-sim-->|s|gps-app-->cfs
+ css-sim-->|s|css-app-->cfs
+ gps-->|uart:1|uart-bus
+
+ subgraph gps
+ gps-sim
+ end
+
+ subgraph cfs
+ gps-app;
+ end
+
+ fortytwo[fortytwo]-->|tx:4277|css
+ %% css-->|64|i2c:2
+
+ fortytwo[fortytwo]-->|tx:4279|mag
+
+ fortytwo[fortytwo]-->|tx:9999|truth42sim
+ truth42sim-->|tx:5111|yamcs
+
+ fortytwo[fortytwo]-->|tx:4284|fss
+
+ fortytwo[fortytwo]-->|tx:4281|imu
+
+ fortytwo[fortytwo]-->|tx:4282|star-tracker
+
+ fortytwo[fortytwo]-->|tx:4283|eps
+
+ radio-->|ci:5010|nos_fsw
+ radio-->|to:5011|nos_fsw
+ radio-->|radio:5015|nos_fsw
+
+ yamcs-->|tc:6010|cryptolib-->radio
+
+ nos_fsw-->|command:5014|radio
+
+ radio-->|tm:6011|yamcs
+ yamcs-->|tc:8010|radio
+
+ %% camsim-->|60|i2c:2
+ %% camsim-->|0|spi
+
+ nos_engine_server-->|tcp:12001|time-->gps
+ stdio-terminal-->|udp:5555|x
+ udp-terminal-->|udp:5556|x
+ %% sample-sim-->|uart:16|uart-bus
+
+ %% actuators
+ %% sensors
+
+ subgraph time_sync
+ time
+
+end
+
+
+ subgraph reaction_wheels
+ rw0;
+ rw1;
+ rw2;
+ end
+
+ subgraph actuators
+ reaction_wheels
+ thruster;
+ torquer;
+ end
+
+ subgraph ground_segment
+ yamcs;
+ terminals;
+ cryptolib;
+ end
+
+ subgraph physics_engine
+ fortytwo
+ end
+
+ subgraph terminals
+ stdio-terminal;
+ udp-terminal;
+ end
+
+ subgraph fsw
+ actuators
+ mag
+ mag
+ fss
+ css
+ camsim
+ eps
+ gps
+ star-tracker
+ imu
+ thruster
+ torquer
+ radio
+ sample-sim
+ uart-bus
+ nos_fsw
+ end
+
+ subgraph space_segment
+ physics_engine
+ fsw
+ truth42sim
+ end
+ %% subgraph sensors
+ %% start-tracker
+ %% end
+
+ end
+
+ classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000;
+ classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff;
+ classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5;
+ classDef rw fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff;
+
+ class fortytwo,gps,stdio-terminal,udp-terminal,sample-sim,eps,torquer,thruster,camsim,yamcs,cryptolib,css,mag,fss,imu,eps,star-tracker k8s;
+
+ class truth42sim k8s;
+ class nos_engine_server k8s;
+ class nos_fsw k8s;
+
+ class rw0,rw1,rw2 rw;
+
+ class client plain;
+ class cluster cluster;
+```
+
+## docker ps
+
+```bash
+docker ps | grep -v 'CONTAINER ID' |wc -l
+24
+
+```
+
+```bash
+docker ps
+
+CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
+3a130fadaea9 ivvitc/nos3-64:20250217 "./nos3-single-simul…" 41 seconds ago Up 40 seconds nos_time_driver
+9661c0fa0ac8 ivvitc/nos3-64:20250217 "./nos3-single-simul…" 49 seconds ago Up 47 seconds sc_1_torquer_sim
+4ff3a142614a ivvitc/nos3-64:20250217 "./nos3-single-simul…" 50 seconds ago Up 47 seconds sc_1_thruster_sim
+277b46405250 ivvitc/nos3-64:20250217 "./nos3-single-simul…" 50 seconds ago Up 48 seconds sc_1_startrk_sim
+44d1809986f5 ivvitc/nos3-64:20250217 "./nos3-single-simul…" 50 seconds ago Up 48 seconds sc_1_sample_sim
+dcde3f725275 ivvitc/nos3-64:20250217 "./nos3-single-simul…" 51 seconds ago Up 47 seconds 0.0.0.0:16010->6010/tcp, 0.0.0.0:16010->6010/udp, 0.0.0.0:16011->6011/tcp, 0.0.0.0:16011->6011/udp sc_1_radio_sim
+1e0a92b988c3 ivvitc/nos3-64:20250217 "./nos3-single-simul…" 51 seconds ago Up 48 seconds sc_1_rw_sim2
+eb3bc34fcc2f ivvitc/nos3-64:20250217 "./nos3-single-simul…" 51 seconds ago Up 49 seconds sc_1_rw_sim1
+0c327e64ef4e ivvitc/nos3-64:20250217 "./nos3-single-simul…" 52 seconds ago Up 50 seconds sc_1_rw_sim0
+9c6166335f7f ivvitc/nos3-64:20250217 "./nos3-single-simul…" 52 seconds ago Up 50 seconds sc_1_mag_sim
+523b92a28e7f ivvitc/nos3-64:20250217 "./nos3-single-simul…" 53 seconds ago Up 50 seconds sc_1_imu_sim
+82dfa03e9c62 ivvitc/nos3-64:20250217 "./nos3-single-simul…" 53 seconds ago Up 51 seconds sc_1_gps_sim
+af58b5d09af2 ivvitc/nos3-64:20250217 "./nos3-single-simul…" 53 seconds ago Up 51 seconds sc_1_fss_sim
+42fe85609195 ivvitc/nos3-64:20250217 "./nos3-single-simul…" 54 seconds ago Up 51 seconds sc_1_eps_sim
+ac7787f9cf7a ivvitc/nos3-64:20250217 "./nos3-single-simul…" 54 seconds ago Up 51 seconds sc_1_css_sim
+5aedf4c0a619 ivvitc/nos3-64:20250217 "./nos3-single-simul…" 56 seconds ago Up 53 seconds sc_1_truth42sim
+0282c8fb48e2 ivvitc/nos3-64:20250217 "/usr/bin/nos_engine…" 57 seconds ago Up 53 seconds sc_1_nos_engine_server
+14e98992d60b ivvitc/nos3-64:20250217 "./support/standalone" 57 seconds ago Up 54 seconds sc_1_cryptolib
+0c06e9b85d03 ivvitc/nos3-64:20250217 "/opt/neo/nos3/scrip…" 57 seconds ago Up 53 seconds sc_1_nos_fsw
+1e84eafbabe1 ivvitc/nos3-64:20250217 "/opt/neo/nos3/scrip…" 58 seconds ago Up 54 seconds sc_1_onair
+2365e959b0c6 ivvitc/nos3-64:20250217 "/home/neo/.nos3/42/…" 58 seconds ago Up 55 seconds sc_1_fortytwo
+72c26bc3690a ivvitc/nos3-64:20250217 "./nos3-single-simul…" 59 seconds ago Up 58 seconds nos_udp_terminal
+96c8948f8b01 ivvitc/nos3-64:20250217 "./nos3-single-simul…" 59 seconds ago Up 58 seconds nos_terminal
+f78f9bda0e0a ivvitc/nos3-64:20250217 "mvn -Dmaven.repo.lo…" About a minute ago Up 58 seconds 0.0.0.0:5012->5012/tcp, 0.0.0.0:8090->8090/tcp cosmos_openc3-operator_1
+
+```
+
+## docker network list
+
+```bash
+
+docker network list
+
+NETWORK ID NAME DRIVER SCOPE
+2a772b648d00 bridge bridge local
+5fe825f9aaa9 docker_quickstart bridge local
+6652439bc0af host host local
+8cd5a6aac2de none null local
+6c6e8d8284e4 nos3_core bridge local
+cca8286b3dd6 nos3_sc_1 bridge local
+
+```
diff --git a/deployments/Taskfile.yaml b/deployments/Taskfile.yaml
new file mode 100644
index 000000000..7758a24da
--- /dev/null
+++ b/deployments/Taskfile.yaml
@@ -0,0 +1,869 @@
+version: '3'
+
+dotenv:
+ - ./.env
+
+vars:
+ PROJECT: '{{.PROJECT | default "nos3"}}'
+ FLEET: '{{.PROJECT | default "nos3"}}'
+ MISSION: '{{.MISSION | default "m01"}}'
+ SPACECRAFT: '{{.SPACECRAFT | default "sc01"}}'
+
+ PROJECT_NAME: '{{.PROJECT}}-{{.MISSION}}-{{.SPACECRAFT}}'
+ PROJECT_MISSION: '{{.PROJECT}}-{{.MISSION}}'
+
+ COMPOSE_PROJECT_NAME: '{{.PROJECT}}-{{.MISSION}}-{{.SPACECRAFT}}'
+ SC_ENVIRO: '{{.SC_ENVIRO | default "sim"}}'
+
+ YAMCS_HOST_PORT: '{{.YAMCS_HOST_PORT | default "8090"}}'
+ OPENMCT_HOST_PORT: '{{.OPENMCT_HOST_PORT | default "9000"}}'
+ FORTYTWO_HOST_PORT: '{{.FORTYTWO_HOST_PORT | default "30090"}}'
+
+ SERVICES_DIR: ./services
+ SCRIPTS_DIR: ./scripts
+ TEMPLATES_DIR: ./templates
+ BUILD_DIR: ./build
+ ENVIROS_DIR: '{{.BUILD_DIR}}/'
+
+ CONTAINER_BIN: docker
+ CONTAINER_COMPOSE_BIN: ' {{.CONTAINER_BIN}} compose '
+
+ CONTAINER_DIR: '.'
+ COMPOSE_FILE: '{{.CONTAINER_DIR}}/compose.yaml'
+
+# Variables for configuring builds and deployments
+ DEPLOYMENT_ENVIRO: '{{.DEPLOYMENT_ENVIRO | default ""}}'
+ HTTP_PROXY: '{{.HTTP_PROXY | default ""}}'
+ HTTPS_PROXY: '{{.HTTPS_PROXY | default ""}}'
+
+ ENVIRO_SCRIPT: '{{.SCRIPTS_DIR}}/env.sh'
+ ENVIRO_FILE: '{{.CONTAINER_DIR}}/.env'
+ ENVIRO_TEMPLATE: '{{.TEMPLATES_DIR}}/env.template'
+
+ MAVEN_HTTPS_PROXY: '-s./settings.xml'
+ MAVEN_SETTINGS_FILE: '{{.MAVEN_SETTINGS_FILE | default ""}}'
+
+ # NOS3 configuration path
+ NOS3_CFG_PATH: '{{.NOS3_CFG_PATH | default ".."}}'
+
+ # Kubernetes variables
+ K8S_ENV_FILE: '{{.K8S_ENV_FILE | default "./targets/kubernetes/env/k8s.env"}}'
+ K8S_CONTEXT: '{{.K8S_CONTEXT | default "docker-desktop"}}'
+ K8S_CLUSTER: '{{.K8S_CLUSTER | default "docker-desktop"}}'
+ K8S_NAMESPACE: '{{.PROJECT}}-{{.MISSION}}'
+ K8S_USER: '{{.K8S_USER | default "docker-desktop"}}'
+
+ # TODO: COMPONENT doesn't make sense here
+ K8S_APP_LABEL: "{{.PROJECT_NAME}}-fortytwo"
+ K8S_PORT_FORWARD_VNC: 8080
+ K8S_KOMPOSE_OUTPUT: '{{.K8S_KOMPOSE_OUTPUT | default "./targets/kubernetes/kompose/"}}'
+ K8S_CONFIGMAP_NAME: '{{.K8S_APP_LABEL}}'
+ K8S_REPLICAS: 1
+
+ K8S_BUILD_PATH: './build/k8s/contexts/{{.K8S_CONTEXT}}/clusters/{{.K8S_CLUSTER}}/namespaces/{{.K8S_NAMESPACE}}/applications/{{.K8S_APP_LABEL}}'
+# K8S_BUILD_PATH: './services/fortytwo/kubernetes'
+
+tasks:
+ default:
+ desc: Shows this help message
+ cmds:
+ - task --list-all
+ silent: true
+
+ test:
+ env:
+ PROJECT: '{{.PROJECT}}'
+ FLEET: '{{.FLEET}}'
+ MISSION: '{{.MISSION}}'
+ SPACECRAFT: '{{.SPACECRAFT}}'
+ PROJECT_MISSION: '{{.PROJECT}}-{{.MISSION}}'
+ PROJECT_NAME: '{{.PROJECT}}-{{.MISSION}}-{{.SPACECRAFT}}'
+ COMPOSE_PROJECT_NAME: '{{.PROJECT}}-{{.MISSION}}-{{.SPACECRAFT}}'
+
+ SC_ENVIRO: '{{.SC_ENVIRO}}'
+
+ FORTYTWO_HOST: '{{.FORTYTWO_HOST}}'
+ FORTYTWO_PORT: '{{.FORTYTWO_PORT}}'
+ FORTYTWO_HOST_PORT: '{{.FORTYTWO_HOST_PORT}}'
+
+ YAMCS_HOST: '{{.YAMCS_HOST}}'
+ YAMCS_PORT: '{{.YAMCS_PORT}}'
+ YAMCS_HOST_PORT: '{{.YAMCS_HOST_PORT}}'
+
+ OPENMCT_HOST: '{{.OPENMCT_HOST}}'
+ OPENMCT_PORT: '{{.OPENMCT_PORT}}'
+ OPENMCT_HOST_PORT: '{{.OPENMCT_HOST_PORT}}'
+
+ ENV_PATH: '{{.BUILD_DIR}}/docker/{{.PROJECT}}/{{.MISSION}}/{{.SPACECRAFT}}/'
+ ENV_SHELL: '{{.PROJECT}}-{{.MISSION}}-{{.SPACECRAFT}}.sh'
+
+ cmds:
+ - echo "project = ${PROJECT}"
+ - echo "fleet = ${FLEET}"
+ - echo "mission = ${MISSION}"
+ - echo "spacecraft = ${SPACECRAFT}"
+ - echo "compose project name = ${COMPOSE_PROJECT_NAME}"; echo
+ - echo "spacecraft enviro = ${SC_ENVIRO}"; echo
+ - echo "fortytwo host = ${FORTYTWO_HOST}"
+ - echo "fortytwo port = ${FORTYTWO_PORT}"
+ - echo "fortytwo host port = ${FORTYTWO_HOST_PORT}"; echo
+ - echo "yamcs host = ${YAMCS_HOST}"
+ - echo "yamcs port = ${YAMCS_PORT}"
+ - echo "yamcs host port = ${YAMCS_HOST_PORT}";echo
+ - echo "openmct host = ${OPENMCT_HOST}"
+ - echo "openmct port = ${OPENMCT_PORT}"
+ - echo "openmct host port = ${OPENMCT_HOST_PORT}";echo
+ - echo "env path = ${ENV_PATH}"
+ - echo "env shell = ${ENV_SHELL}"; echo
+ - |
+ mkdir -p ${ENV_PATH}
+
+ {{.ENVIRO_SCRIPT}} > ${ENV_PATH}/${ENV_SHELL}.env
+
+ cp ${ENV_PATH}/${ENV_SHELL}.env .env
+ - task: up
+
+ silent: true
+
+ build-fortytwo:
+ desc: Build fortytwo (42)
+ deps:
+ - env:create
+ - set-permissions
+ cmds:
+ - |
+ source {{.ENVIRO_FILE}} && \
+ {{.CONTAINER_COMPOSE_BIN}} -f {{.COMPOSE_FILE}} build --pull nos3-fortytwo
+ silent: true
+
+ build-yamcs:
+ desc: Build yamcs
+ deps:
+ - env:create
+ - setup-maven
+ - set-permissions
+ cmds:
+ - |
+ source {{.ENVIRO_FILE}} && \
+ {{.CONTAINER_COMPOSE_BIN}} -f {{.COMPOSE_FILE}} build --pull nos3-gsw
+ silent: true
+
+ build-openmct:
+ desc: Build openmct
+ deps:
+ - env:create
+ - set-permissions
+ cmds:
+ - '{{.CONTAINER_BIN}} pull docker.io/library/ubuntu:25.04'
+ - |
+ source {{.ENVIRO_FILE}} && \
+ {{.CONTAINER_COMPOSE_BIN}} -f {{.COMPOSE_FILE}} build --pull nos3-openmct
+ silent: true
+
+ build-fsw:
+ desc: Build nos3-base-local (fsw)
+ deps:
+ - env:create
+ - set-permissions
+ cmds:
+ - |
+ source {{.ENVIRO_FILE}} && \
+ {{.CONTAINER_COMPOSE_BIN}} -f {{.COMPOSE_FILE}} build --pull nos3-fsw
+ silent: true
+
+ build:
+ desc: Build all containers
+ deps:
+ - env:create
+ - set-permissions
+ - setup-maven
+ cmds:
+ - '{{.CONTAINER_BIN}} pull docker.io/library/ubuntu:25.04'
+ - |
+ source {{.ENVIRO_FILE}} && \
+ {{.CONTAINER_COMPOSE_BIN}} -f {{.COMPOSE_FILE}} build --pull nos3-fortytwo nos3-gsw nos3-openmct nos3-fsw
+ silent: true
+
+ set-permissions:
+ desc: set filesystem permissions, needed for restricted accounts
+ cmds:
+ - mkdir -p ../cfg/build
+ - find . -type f -name '*.sh' -exec chmod 777 {} \; # Make sure all scripts are world executable
+ - find . -type f -name '*.py' -exec chmod 777 {} \; # Make sure all scripts are world executable
+ - find . -type f -name '*.json' -exec chmod 666 {} \; # Make sure all .json files are world readable
+ - find . -type f -name '*.xml' -exec chmod 666 {} \; # Make sure all .xml files are world readable
+ - find . -type f -name '*.c' -exec chmod 555 {} \; # Make sure all .xml files are world readable
+ - find . -type f -name '*.h' -exec chmod 555 {} \; # Make sure all .xml files are world readable
+ - find ../cfg/build -type f -name '*.sh' -exec chmod 777 {} \; # Make sure all scripts are world executable
+ - find ../cfg/build -type f -name '*.py' -exec chmod 777 {} \; # Make sure all scripts are world executable
+ - find ../cfg/build -type f -name '*.json' -exec chmod 666 {} \; # Make sure all .json files are world readable
+ - find ../cfg/build -type f -name '*.xml' -exec chmod 666 {} \; # Make sure all .xml files are world readable
+ - find ${HOME}/.nos3/ -type f -name '*.xml' -exec chmod 666 {} \; # Make sure all .xml files are world readable
+ silent: true
+
+ up:
+ desc: Bring up nos3 in detached state
+ # env:
+ # PROJECT: '{{.PROJECT}}'
+ # FLEET: '{{.FLEET}}'
+ # MISSION: '{{.MISSION}}'
+ # SPACECRAFT: '{{.SPACECRAFT}}'
+ # PROJECT_MISSION: '{{.PROJECT}}-{{.MISSION}}'
+ # PROJECT_NAME: '{{.PROJECT}}-{{.MISSION}}-{{.SPACECRAFT}}'
+ # COMPOSE_PROJECT_NAME: '{{.PROJECT}}-{{.MISSION}}-{{.SPACECRAFT}}'
+
+ # SC_ENVIRO: '{{.SC_ENVIRO}}'
+
+ # FORTYTWO_HOST: '{{.FORTYTWO_HOST}}'
+ # FORTYTWO_PORT: '{{.FORTYTWO_PORT}}'
+ # FORTYTWO_HOST_PORT: '{{.FORTYTWO_HOST_PORT}}'
+
+ # YAMCS_HOST: '{{.YAMCS_HOST}}'
+ # YAMCS_PORT: '{{.YAMCS_PORT}}'
+ # YAMCS_HOST_PORT: '{{.YAMCS_HOST_PORT}}'
+
+ # OPENMCT_HOST: '{{.OPENMCT_HOST}}'
+ # OPENMCT_PORT: '{{.OPENMCT_PORT}}'
+ # OPENMCT_HOST_PORT: '{{.OPENMCT_HOST_PORT}}'
+
+ # ENV_PATH: '{{.BUILD_DIR}}/docker/${PROJECT}/${MISSION}/${SPACECRAFT}/'
+ # ENV_SHELL: '${PROJECT}-${MISSION}-${SPACECRAFT}.sh'
+
+ cmds:
+ - task: submodule-update
+ - task: env:generate
+ - task: build
+ - task: config-nos3-mission
+ - task: nos3:config
+ - task: set-permissions
+ - |
+ source {{.ENVIRO_FILE}} && \
+ export NOS3_CFG_PATH={{.NOS3_CFG_PATH}}; \
+ {{.CONTAINER_COMPOSE_BIN}} --env-file {{.ENVIRO_FILE}} -f {{.COMPOSE_FILE}} --project-name {{.COMPOSE_PROJECT_NAME}} up -d --remove-orphans
+ - task check-service-ready SERVICE_HOST={{.YAMCS_HOST}} SERVICE_PORT={{.YAMCS_HOST_PORT}}
+ - sleep 5
+ - task sidecar
+ - task info-services
+ - task check-docker-ps
+ silent: true
+
+ down:
+ desc: Bring down nos3
+ cmds:
+ - |
+ {{.CONTAINER_COMPOSE_BIN}} -f {{.COMPOSE_FILE}} --project-name {{.COMPOSE_PROJECT_NAME}} down --remove-orphans || true
+ silent: true
+
+ up-minimum:
+ desc: Bring up nos3 (minimum) in detached state
+ deps:
+ - down
+ - env:create
+ - build
+ - config-nos3-mission
+ - nos3:config
+ - set-permissions
+ cmds:
+ - sleep 5
+ - |
+ source {{.ENVIRO_FILE}} && \
+ export NOS3_CFG_PATH={{.NOS3_CFG_PATH}} ; \
+ {{.CONTAINER_COMPOSE_BIN}} \
+ --env-file {{.ENVIRO_FILE}} \
+ -f {{.COMPOSE_FILE}} --project-name {{.COMPOSE_PROJECT_NAME}} up -d \
+ nos3-fortytwo \
+ nos3-nos-engine-server \
+ nos3-fsw \
+ nos3-gps-sim \
+ nos3-time \
+ nos3-radio-sim-cryptolib \
+ nos3-truth42sim \
+ nos3-gsw \
+ nos3-openmct \
+ --force-recreate \
+ --remove-orphans
+ - task check-service-ready SERVICE_HOST=${YAMCS_HOST} SERVICE_PORT=${YAMCS_HOST_PORT}
+ - task sidecar
+ - task info-services
+ - task check-docker-ps
+ silent: true
+
+ sidecar:
+ desc: Run sidecar to enable output
+ cmds:
+ - '{{.SCRIPTS_DIR}}/sidecar.sh -s {{.YAMCS_HOST}} -p {{.YAMCS_HOST_PORT}} -c "/CFS/CMD/TO_ENABLE_OUTPUT"'
+ silent: true
+
+ clean-containers:
+ desc: clean images, cache, volumes, and networks
+ deps:
+ - down
+ cmds:
+ - yes | {{.CONTAINER_BIN}} image prune --filter "dangling=true"
+ - yes | {{.CONTAINER_BIN}} system prune -a
+ - yes | {{.CONTAINER_BIN}} volume prune
+ - yes | {{.CONTAINER_BIN}} network prune
+ silent: true
+
+ setup-maven:
+ desc: Setup maven settings.xml with proxy info-services
+ cmds:
+ - echo "" > {{.SERVICES_DIR}}/yamcs/settings.xml
+ - |
+ if [ "{{.DEPLOYMENT_ENVIRO}}" == "vmmoc" ]; then
+ cp -f {{.MAVEN_SETTINGS_FILE}} ${HOME}/.nos3/yamcs/ || true
+ cp -f {{.MAVEN_SETTINGS_FILE}} {{.SERVICES_DIR}}/yamcs/ || true
+ fi
+ silent: true
+
+ check-docker-ps:
+ desc: check docker ps for any exited|unhealthy|created|error containers
+ cmds:
+ - |
+ {{.CONTAINER_BIN}} ps -a | egrep -i 'exited|unhealthy|created|error' | grep -v STATUS || true
+ silent: true
+
+ check-service-ready:
+ env:
+ SERVICE_HOST: '{{.SERVICE_HOST | default "{{.YAMCS_HOST}}"}}'
+ SERVICE_PORT: '{{.SERVICE_PORT | default "{{.YAMCS_HOST_PORT}}"}}'
+ desc: wait for a service to be ready on a given port
+ cmds:
+ - |
+ echo "Waiting for service {{.SERVICE_HOST}}:{{.SERVICE_PORT}} to be ready..."
+ COUNT=0
+ until nc -z {{.SERVICE_HOST}} {{.SERVICE_PORT}}; do
+ COUNT=$((COUNT + 1))
+ if [ $COUNT -ge 60 ]; then
+ echo "Timeout waiting for port {{.SERVICE_PORT}} to open"
+ exit 1
+ fi
+ sleep 1
+ echo "Waiting for {{.SERVICE_HOST}}:{{.SERVICE_PORT}} to be ready... ($COUNT/60)"
+ done
+ echo "Port {{.SERVICE_HOST}}:{{.SERVICE_PORT}} is open, checking API..."
+ COUNT=0
+ until curl -s "http://{{.SERVICE_HOST}}:{{.SERVICE_PORT}}/api" | grep -q '"yamcsVersion"'; do
+ COUNT=$((COUNT + 1))
+ if [ $COUNT -ge 60 ]; then
+ echo "Timeout waiting for API to be ready"
+ exit 1
+ fi
+ sleep 1
+ echo "Waiting for API to be ready... ($COUNT/60)"
+ done
+ echo "Service http://{{.SERVICE_HOST}}:{{.SERVICE_PORT}}/api is ready!"
+ silent: true
+
+ purge-containers:
+ desc: 'WARNING: purge docker/podman images, cache, volumes, and networks'
+ deps:
+ - down
+ cmds:
+ - yes | {{.CONTAINER_BIN}} image prune --filter "dangling=true" || true
+ - yes | {{.CONTAINER_BIN}} system prune -a || true
+ - yes | {{.CONTAINER_BIN}} volume prune || true
+ - yes | {{.CONTAINER_BIN}} network prune || true
+ - echo
+ silent: true
+
+ info-services:
+ desc: info on accessing various services
+ cmds:
+ - task env:create
+ - |
+ source {{.ENVIRO_FILE}}
+ echo; echo "How to access various services:"; echo; \
+ echo "open http://localhost:${YAMCS_HOST_PORT} to access yamcs/gsw"; \
+ echo "open http://localhost:${FORTYTWO_HOST_PORT}/vnc.html to access fortytwo (42) and then click Connect"; \
+ echo "FT: open http://localhost:${OPENMCT_HOST_PORT}/#/browse/taxonomy:spacecraft/taxonomy:~SIM_42_TRUTH/taxonomy:~SIM_42_TRUTH~SIM_42_TRUTH_DATA/taxonomy:~SIM_42_TRUTH~SIM_42_TRUTH_DATA~BVB_2?tc.mode=fixed&tc.timeSystem=utc&tc.startBound=${SIM_T0_EPOCH_MILLISECONDS}&tc.endBound=${SIM_TF_EPOCH_MILLISECONDS}&view=plot-single"
+ echo "RT: open http://localhost:${OPENMCT_HOST_PORT}/#/browse/taxonomy:spacecraft/taxonomy:~SIM_42_TRUTH/taxonomy:~SIM_42_TRUTH~SIM_42_TRUTH_DATA/taxonomy:~SIM_42_TRUTH~SIM_42_TRUTH_DATA~BVB_2?tc.mode=local&tc.timeSystem=utc&view=plot-single&tc.startDelta=60000&tc.endDelta=300000"
+ echo
+ silent: true
+
+ env:create:
+ desc: Create .env file from env.sh script
+ # cmds:
+ # - |
+ # {{.ENVIRO_SCRIPT}} > {{.ENVIRO_FILE}}
+ cmds:
+ - task env:generate
+ silent: true
+
+ install-lazydocker:
+ desc: Install lazydocker CLI tool (to view logs, etc)
+ cmds:
+ - |
+ curl --silent https://raw.githubusercontent.com/jesseduffield/lazydocker/master/scripts/install_update_linux.sh | bash
+ - |
+ echo; echo "don't forget to add: ${HOME}/.local/bin:${PATH} to your ~/.bashrc or ~/.zshrc"; echo
+ - |
+ echo "watch a Lazydocker demo here: https://www.youtube.com/watch?v=NICqQPxwJWw"; echo
+ silent: true
+
+ config-nos3-mission:
+ desc: configure nos3-mission.xml to set start-time
+ deps:
+ - env:create
+ cmds:
+ - |
+ source {{.ENVIRO_FILE}} && \
+ sed -Ei 's/(\s*)(\).*(\)/\1\2>'${START_TIME}'<\/\3/g' ../cfg/nos3-mission.xml && \
+ sed -Ei 's/(\s*)(\).*(\)/\1\2>'${GSW_SOFTWARE}'<\/\3/g' ../cfg/nos3-mission.xml && \
+ sed -Ei 's/(\s*)(\).*(\)/\1\2>'${FSW_SOFTWARE}'<\/\3/g' ../cfg/nos3-mission.xml
+ silent: true
+
+ submodule-update:
+ desc: sync and update git submodules
+ cmds:
+ - git submodule sync; git submodule update --init --recursive; git submodule sync
+ silent: true
+
+ env:generate:
+ desc: generate env.sh file
+ cmds:
+ - |
+ export PROJECT={{.PROJECT}}
+ export FLEET={{.FLEET}}
+ export MISSION={{.MISSION}}
+ export SPACECRAFT={{.SPACECRAFT}}
+
+ export PROJECT_MISSION={{.PROJECT}}-{{.MISSION}}
+ export PROJECT_NAME={{.PROJECT}}-{{.MISSION}}-{{.SPACECRAFT}}
+ export COMPOSE_PROJECT_NAME={{.PROJECT}}-{{.MISSION}}-{{.SPACECRAFT}}
+
+ export SC_ENVIRO={{.SC_ENVIRO}}
+
+ export FORTYTWO_HOST={{.FORTYTWO_HOST}}
+ export FORTYTWO_PORT={{.FORTYTWO_PORT}}
+ export FORTYTWO_HOST_PORT={{.FORTYTWO_HOST_PORT}}
+
+ export YAMCS_HOST={{.YAMCS_HOST}}
+ export YAMCS_PORT={{.YAMCS_PORT}}
+ export YAMCS_HOST_PORT={{.YAMCS_HOST_PORT}}
+
+ export OPENMCT_HOST={{.OPENMCT_HOST}}
+ export OPENMCT_PORT={{.OPENMCT_PORT}}
+ export OPENMCT_HOST_PORT={{.OPENMCT_HOST_PORT}}
+
+ export ENV_PATH={{.BUILD_DIR}}/docker/${PROJECT}/${MISSION}/${SPACECRAFT}/
+ export ENV_SHELL=${PROJECT}-${MISSION}-${SPACECRAFT}.sh
+
+ mkdir -p ${ENV_PATH}
+
+ {{.ENVIRO_SCRIPT}} > ${ENV_PATH}/${ENV_SHELL}.env
+
+ cp ${ENV_PATH}/${ENV_SHELL}.env .env
+
+ silent: true
+
+# nos3 native tasks
+ nos3:config:
+ desc: configure nos3 by running config.sh
+ cmds:
+ - cd ../ && ./scripts/cfg/config.sh
+ silent: true
+
+ nos3:clean:
+ desc: clean nos3 configuration
+ deps:
+ - down
+ cmds:
+ - cd ../ && make clean
+ silent: true
+
+ nos3:uninstall:
+ desc: uninstall nos3 items
+ deps:
+ - down
+ - nos3:clean
+ cmds:
+ - cd ../ && make uninstall
+ silent: true
+
+# K8S Kubernetes Tasks
+ k8s:create:namespace:
+ desc: Create the Kubernetes namespace
+ deps:
+ - env:create
+ cmds:
+ - |
+ source {{.ENVIRO_FILE}} && \
+ kubectl create namespace {{.K8S_NAMESPACE}} \
+ --context {{.K8S_CONTEXT}} || \
+ echo "Namespace {{.K8S_NAMESPACE}} in context {{.K8S_CONTEXT}} already exists"
+ - echo "Initiated creation of Namespace [{{.K8S_NAMESPACE}}] in context [{{.K8S_CONTEXT}}]"
+ silent: true
+
+ k8s:apply:kustomization:
+ desc: Apply kustomization to the namespace
+ deps:
+ - k8s:create:namespace
+ - k8s:generate:all
+ cmds:
+ - |
+ source {{.ENVIRO_FILE}} && \
+ kubectl apply -k {{.K8S_BUILD_PATH}} \
+ --context {{.K8S_CONTEXT}} \
+ --namespace {{.K8S_NAMESPACE}}
+ - echo "Initiated apply of Kustomization in path [{{.K8S_BUILD_PATH}}] for context [{{.K8S_CONTEXT}}] and namespace [{{.K8S_NAMESPACE}}]"
+ silent: true
+
+ k8s:delete:kustomization:
+ desc: Delete kustomize and namespace
+ cmds:
+ - |
+ source {{.ENVIRO_FILE}} && \
+ kubectl delete -k {{.K8S_BUILD_PATH}} \
+ --context {{.K8S_CONTEXT}} \
+ --namespace={{.K8S_NAMESPACE}}|| true
+ - echo "Initiated deletion of Kustomization for context [{{.K8S_CONTEXT}}] and namespace [{{.K8S_NAMESPACE}}]"
+ - task: k8s:delete:namespace
+ silent: true
+
+ k8s:convert:kompose:
+ desc: convert docker-compose to kubernetes manifests using kompose
+ deps:
+ - env:create
+ cmds:
+ - |
+ source {{.ENVIRO_FILE}} && \
+ mkdir -p {{.K8S_KOMPOSE_OUTPUT}} && \
+ kompose convert -f {{.COMPOSE_FILE}} -o {{.K8S_KOMPOSE_OUTPUT}}
+ silent: true
+
+ k8s:convert:kompose:helm:
+ desc: convert docker-compose to kubernetes manifests using kompose
+ deps:
+ - env:create
+ cmds:
+ - |
+ source {{.ENVIRO_FILE}} && \
+ mkdir -p {{.K8S_KOMPOSE_OUTPUT}} && \
+ kompose convert -c -f {{.COMPOSE_FILE}} -o {{.K8S_KOMPOSE_OUTPUT}}
+ silent: true
+
+ k8s:delete:namespace:
+ desc: delete nos3 kubernetes namespace
+ deps:
+ - env:create
+ cmds:
+ - |
+ source {{.ENVIRO_FILE}} && \
+ kubectl delete namespace {{.K8S_NAMESPACE}} --context {{.K8S_CONTEXT}} || true
+ silent: true
+
+ k8s:apply:deployment:
+ desc: apply nos3 kubernetes deployment(s)
+ deps:
+ - env:create
+ - k8s:create:namespace
+ cmds:
+ - |
+ source {{.ENVIRO_FILE}} && \
+ kubectl apply -f ./targets/kubernetes/kompose/ --context {{.K8S_CONTEXT}} --namespace={{.K8S_NAMESPACE}}
+ silent: true
+
+ k8s:delete:deployment:
+ desc: delete nos3 kubernetes deployment(s)
+ deps:
+ - env:create
+ cmds:
+ - |
+ source {{.ENVIRO_FILE}} && \
+ kubectl delete --all deployments --context {{.K8S_CONTEXT}} --namespace={{.K8S_NAMESPACE}} || true
+
+ k8s:get:configmaps:
+ desc: get nos3 kubernetes configmaps
+ deps:
+ - env:create
+ cmds:
+ - |
+ source {{.ENVIRO_FILE}} && \
+ kubectl get configmaps --context {{.K8S_CONTEXT}} --namespace={{.K8S_NAMESPACE}} || true
+ silent: true
+
+ k8s:get:configmap:
+ desc: get a specific kubernetes configmaps defined in K8S_CONFIGMAP_NAME
+ deps:
+ - env:create
+ cmds:
+ - |
+ source {{.ENVIRO_FILE}} && \
+ kubectl get configmap --context {{.K8S_CONTEXT}} --namespace={{.K8S_NAMESPACE}} {{.K8S_CONFIGMAP_NAME}} -o yaml
+ silent: true
+
+ k8s:apply:ingress:nginx:
+ desc: apply ingress-nginx for nos3 kubernetes
+ cmds:
+ - |
+ source {{.ENVIRO_FILE}} && \
+ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.14.1/deploy/static/provider/cloud/deploy.yaml || true
+ silent: true
+
+ k8s:get:pod-name:
+ desc: get the pod name for nos3 kubernetes deployment
+ deps:
+ - env:create
+ cmds:
+ - |
+ source {{.ENVIRO_FILE}} && \
+ POD_NAME=$(kubectl get pods \
+ --context {{.K8S_CONTEXT}} \
+ --namespace={{.K8S_NAMESPACE}} \
+ -l app={{.K8S_APP_LABEL}} \
+ -o jsonpath='{.items[0].metadata.name}' 2>/dev/null); \
+ echo $POD_NAME
+ silent: true
+
+ k8s:port-forward:
+ desc: Port forward to access services locally
+ deps:
+ - env:create
+ cmds:
+ - |
+ source {{.ENVIRO_FILE}} && \
+ echo "Checking for pod availability..."
+ POD_NAME=$(kubectl get pods \
+ --context {{.K8S_CONTEXT}} \
+ --namespace={{.K8S_NAMESPACE}} \
+ -l app={{.K8S_APP_LABEL}} \
+ -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)
+
+ if [ -z "$POD_NAME" ]; then
+ echo "Error: No pod found with label app={{.K8S_APP_LABEL}}"
+ exit 1
+ fi
+
+ echo "Found pod: $POD_NAME"
+ echo "Waiting for pod to be ready..."
+
+ kubectl wait --for=condition=ready pod/$POD_NAME \
+ --context {{.K8S_CONTEXT}} \
+ --namespace={{.K8S_NAMESPACE}} \
+ --timeout=300s
+
+ if [ $? -ne 0 ]; then
+ echo "Error: Pod did not become ready within timeout"
+ exit 1
+ fi
+
+ echo "Pod is ready!"
+ echo "Forwarding ports:"
+ echo " - VNC: http://localhost:{{.K8S_PORT_FORWARD_VNC}}"
+ kubectl port-forward ${POD_NAME} \
+ --context {{.K8S_CONTEXT}} \
+ --namespace={{.K8S_NAMESPACE}} \
+ {{.K8S_PORT_FORWARD_VNC}}:80
+ silent: true
+
+ k8s:kill:port-forward:
+ desc: Kill port-forward
+ deps:
+ - env:create
+ cmds:
+ - |
+ source {{.ENVIRO_FILE}} && \
+ pkill -f 'kubectl port-forward' || true
+ silent: true
+
+ k8s:write:env:
+ desc: write k8s env file
+ cmds:
+ - |
+ echo "HTTP_PROXY={{.HTTP_PROXY}}" > {{.K8S_ENV_FILE}}
+ echo "HTTPS_PROXY={{.HTTPS_PROXY}}" >> {{.K8S_ENV_FILE}}
+ echo "NO_PROXY={{.NO_PROXY}}" >> {{.K8S_ENV_FILE}}
+ silent: true
+
+ k8s:generate:all:
+ desc: Generate all kubernetes yamls for COMPONENT_NAME
+ deps:
+ - k8s:path:yamls
+ - k8s:generate:configmaps:all
+ - k8s:generate:deployment
+ - k8s:generate:service
+ - k8s:generate:ingress
+ - k8s:generate:kustomization
+ silent: true
+
+ k8s:path:yamls:
+ desc: generate kubernetes yamls for the deployment
+ cmds:
+ - mkdir -p {{.K8S_BUILD_PATH}}
+ silent: true
+
+ k8s:generate:configmaps:all:
+ desc: Generate all kubernetes yamls for COMPONENT_NAME
+ deps:
+ - k8s:generate:configmap:env
+ - k8s:generate:configmap:volumes
+ - k8s:generate:configmap:args
+ silent: true
+
+ k8s:generate:configmap:env:
+ desc: Generate kubernetes configmaps yaml for COMPONENT_NAME
+ deps:
+ - env:create
+ - k8s:path:yamls
+ cmds:
+ - |
+ source {{.ENVIRO_FILE}} && \
+ kubectl create configmap \
+ {{.K8S_CONFIGMAP_NAME}} \
+ --from-env-file={{.ENVIRO_FILE}} --dry-run=client -o yaml > {{.K8S_BUILD_PATH}}/configmap-env.yaml
+ silent: true
+
+ k8s:generate:configmap:volumes:
+ desc: Generate kubernetes volumes configmaps yaml for COMPONENT_NAME
+ deps:
+ - env:create
+ - k8s:path:yamls
+ cmds:
+ - |
+ source {{.ENVIRO_FILE}} && \
+ kubectl create configmap {{.K8S_CONFIGMAP_NAME}}-volumes \
+ --from-file=./services/fortytwo/entrypoint.sh \
+ --dry-run=client \
+ --output yaml > {{.K8S_BUILD_PATH}}/configmap-volumes.yaml
+ silent: true
+
+ k8s:generate:configmap:args:
+ desc: Generate kubernetes configmaps yaml for COMPONENT_NAME
+ deps:
+ - env:create
+ - k8s:path:yamls
+ cmds:
+ - |
+ source {{.ENVIRO_FILE}} && \
+ echo "" > {{.K8S_BUILD_PATH}}/configmap-args.yaml
+ silent: true
+
+ k8s:generate:service:
+ desc: Generate kubernetes service yaml for COMPONENT_NAME
+ deps:
+ - env:create
+ - k8s:path:yamls
+ cmds:
+ - |
+ source {{.ENVIRO_FILE}} && \
+ cat < {{.K8S_BUILD_PATH}}/service.yaml
+ ---
+ apiVersion: v1
+ kind: Service
+ metadata:
+ name: {{.K8S_APP_LABEL}}
+
+ spec:
+ selector:
+ app: {{.K8S_APP_LABEL}}
+ ports:
+ - name: x-vnc
+ protocol: TCP
+ port: 80
+ targetPort: 80
+ type: ClusterIP
+ EOF
+ silent: true
+
+ k8s:generate:ingress:
+ desc: Generate kubernetes configmaps yaml for COMPONENT_NAME
+ deps:
+ - env:create
+ - k8s:path:yamls
+ cmds:
+ - |
+ source {{.ENVIRO_FILE}} && \
+ echo "" > {{.K8S_BUILD_PATH}}/ingress.yaml
+ silent: true
+
+ k8s:generate:deployment:
+ desc: Generate kubernetes deployment yaml for COMPONENT_NAME
+ deps:
+ - env:create
+ - k8s:path:yamls
+ cmds:
+ - |
+ source {{.ENVIRO_FILE}} && \
+ cat < {{.K8S_BUILD_PATH}}/deployment.yaml
+ ---
+ apiVersion: apps/v1
+ kind: Deployment
+ metadata:
+ name: {{.K8S_APP_LABEL}}
+ namespace: {{.K8S_NAMESPACE}}
+ labels:
+ app: {{.K8S_APP_LABEL}}
+ spec:
+ replicas: {{.K8S_REPLICAS}}
+ selector:
+ matchLabels:
+ app: {{.K8S_APP_LABEL}}
+ template:
+ metadata:
+ labels:
+ app: {{.K8S_APP_LABEL}}
+ spec:
+ containers:
+ - args:
+ - /entrypoint.sh
+ env:
+ - name: COMPONENT_NAME
+ value: fortytwo
+ - name: DISPLAY
+ value: :1
+ image: ghcr.io/haisamido/nos3-base-fortytwo:dev
+ workingDir: /42
+ imagePullPolicy: Always
+ name: {{.K8S_APP_LABEL}}
+ ports:
+ - containerPort: 80
+ protocol: TCP
+ resources:
+ limits:
+ cpu: "3"
+ memory: "2048Mi"
+ requests:
+ cpu: "1"
+ memory: "2048Mi"
+ securityContext:
+ privileged: true
+ volumeMounts:
+ - mountPath: /entrypoint.sh
+ name: {{.K8S_APP_LABEL}}-volumes
+ subPath: entrypoint.sh
+ hostname: {{.K8S_APP_LABEL}}
+ restartPolicy: Always
+ volumes:
+ - configMap:
+ items:
+ - key: entrypoint.sh
+ path: entrypoint.sh
+ name: {{.K8S_APP_LABEL}}-volumes
+ defaultMode: 0555
+ name: "{{.K8S_APP_LABEL}}-volumes"
+ EOF
+ silent: true
+
+ k8s:generate:kustomization:
+ desc: Generate Kustomization yaml for COMPONENT_NAME
+ deps:
+ - env:create
+ - k8s:path:yamls
+ cmds:
+ - |
+ source {{.ENVIRO_FILE}} && \
+ cat < {{.K8S_BUILD_PATH}}/kustomization.yaml
+ ---
+ apiVersion: kustomize.config.k8s.io/v1beta1
+ kind: Kustomization
+ resources:
+ - ./configmap-args.yaml
+ - ./configmap-env.yaml
+ - ./configmap-volumes.yaml
+ - ./deployment.yaml
+ - ./service.yaml
+ - ./ingress.yaml
+
+ EOF
+ silent: true
diff --git a/deployments/compose.yaml b/deployments/compose.yaml
new file mode 100644
index 000000000..0016ac9f8
--- /dev/null
+++ b/deployments/compose.yaml
@@ -0,0 +1,937 @@
+name: ${COMPOSE_PROJECT_NAME}
+
+services:
+ nos3-fortytwo:
+ image: ghcr.io/haisamido/nos3-base-fortytwo:dev
+ env_file:
+ - ./.env
+ build:
+ context: ./services/fortytwo
+ dockerfile: Dockerfile
+ args:
+ DEPLOYMENT_ENVIRO: ${DEPLOYMENT_ENVIRO}
+ HTTPS_PROXY: ${HTTPS_PROXY}
+ HTTP_PROXY: ${HTTP_PROXY}
+ NO_PROXY: ${NO_PROXY}
+ MAVEN_HTTPS_PROXY: ${MAVEN_HTTPS_PROXY}
+ GIT_URL: ${FORTYTWO_GIT_URL}
+ GIT_COMMIT: ${FORTYTWO_GIT_COMMIT}
+ GIT_FOLDER: ${FORTYTWO_GIT_FOLDER:-42}
+ STARTUP_FOLDER: ${FORTYTWO_STARTUP_FOLDER}
+ RECOMPILE: ${FORTYTWO_RECOMPILE:-false}
+ BASE_DIR: ${FORTYTWO_BASE_DIR}
+ VNC_PASSWORD: ${FORTYTWO_VNC_PASSWORD}
+ healthcheck:
+ test: ["CMD-SHELL", "/usr/bin/pgrep -f 42 | grep -v xterm ; sleep 2"]
+ interval: 10s
+ timeout: 5s
+ retries: 7
+ attach: true
+ container_name: ${COMPOSE_PROJECT_NAME}-fortytwo
+ hostname: ${COMPOSE_PROJECT_NAME}-fortytwo
+ working_dir: "/opt/nasa-itc/"
+ command: '/entrypoint.sh'
+ volumes:
+ - ${PWD}/${NOS3_CFG_PATH}/cfg/build/InOut/:/opt/nasa-itc/42/NOS3InOut/
+ - ${PWD}/${NOS3_CFG_PATH}/cfg/build/InOut/Inp_IPC.txt:/opt/nasa-itc/42/NOS3InOut/Inp_IPC.txt
+ - ${PWD}/${NOS3_CFG_PATH}/cfg/build/InOut/Inp_Sim.txt:/opt/nasa-itc/42/NOS3InOut/Inp_Sim.txt
+ - ${PWD}/services/fortytwo/entrypoint.sh:/entrypoint.sh
+ ports:
+ - "127.0.0.1:${FORTYTWO_HOST_PORT}:${FORTYTWO_PORT}"
+ environment:
+ DEPLOYMENT_ENVIRO: ${DEPLOYMENT_ENVIRO}
+ HTTPS_PROXY: ${HTTPS_PROXY}
+ HTTP_PROXY: ${HTTP_PROXY}
+ NO_PROXY: ${NO_PROXY}
+ MAVEN_HTTPS_PROXY: ${MAVEN_HTTPS_PROXY}
+ GIT_URL: ${FORTYTWO_GIT_URL}
+ GIT_COMMIT: ${FORTYTWO_GIT_COMMIT}
+ GIT_FOLDER: ${FORTYTWO_GIT_FOLDER:-42}
+ STARTUP_FOLDER: ${FORTYTWO_STARTUP_FOLDER}
+ RECOMPILE: ${FORTYTWO_RECOMPILE:-false}
+ BASE_DIR: ${FORTYTWO_BASE_DIR}
+ VNC_PASSWORD: ${FORTYTWO_VNC_PASSWORD}
+ MISSION: ${MISSION}
+ SPACECRAFT: ${SPACECRAFT}
+ PROJECT_NAME: ${PROJECT_NAME}
+ HOST_PORT: ${FORTYTWO_PORT}
+ networks:
+ mission-spacecraft-network:
+ aliases:
+ - fortytwo
+ mission-network:
+
+ nos3-gsw:
+ image: ghcr.io/haisamido/nos3-base-yamcs:dev
+ env_file:
+ - ./.env
+ build:
+ context: ./services/yamcs
+ dockerfile: Dockerfile
+ args:
+ DEPLOYMENT_ENVIRO: ${DEPLOYMENT_ENVIRO}
+ HEALTHCHECK_PORT: ${HEALTHCHECK_PORT}
+ HTTPS_PROXY: ${HTTPS_PROXY}
+ HTTP_PROXY: ${HTTP_PROXY}
+ NO_PROXY: ${NO_PROXY}
+ MAVEN_REPO_LOCAL: ${MAVEN_REPO_LOCAL}
+ MAVEN_HTTPS_PROXY: ${MAVEN_HTTPS_PROXY}
+ COMPONENT_DIR: ${COMPONENT_DIR}
+ YAMCS_DATA_DIR: ${YAMCS_DATA_DIR}
+ GSW_SOFTWARE: ${GSW_SOFTWARE}
+ GIT_URL: ${YAMCS_GIT_URL}
+ GIT_COMMIT: ${YAMCS_GIT_COMMIT}
+ healthcheck:
+ test: ["CMD-SHELL", "curl http://${YAMCS_HOST}:${YAMCS_PORT}/api"]
+ interval: 10s
+ timeout: 5s
+ retries: 10
+ attach: true
+ container_name: ${COMPOSE_PROJECT_NAME}-gsw
+ hostname: ${COMPOSE_PROJECT_NAME}-gsw
+ working_dir: "/home/nos3/.nos3/yamcs"
+ command: "/entrypoint.sh"
+ volumes:
+ - ${PWD}/services/yamcs/entrypoint.sh:/entrypoint.sh
+ - ${PWD}/services/yamcs/settings.xml:/home/nos3/.nos3/yamcs/settings.xml
+ ports:
+ - "127.0.0.1:${YAMCS_HOST_PORT}:${YAMCS_PORT}"
+ tty: true
+ stdin_open: true
+ privileged: true
+ environment:
+ DEPLOYMENT_ENVIRO: ${DEPLOYMENT_ENVIRO}
+ HEALTHCHECK_PORT: ${HEALTHCHECK_PORT}
+ HTTPS_PROXY: ${HTTPS_PROXY}
+ HTTP_PROXY: ${HTTP_PROXY}
+ NO_PROXY: ${NO_PROXY}
+ MAVEN_REPO_LOCAL: ${MAVEN_REPO_LOCAL}
+ MAVEN_HTTPS_PROXY: ${MAVEN_HTTPS_PROXY}
+ COMPONENT_DIR: ${COMPONENT_DIR}
+ YAMCS_DATA_DIR: ${YAMCS_DATA_DIR}
+ GSW_SOFTWARE: ${GSW_SOFTWARE}
+ GIT_URL: ${YAMCS_GIT_URL}
+ GIT_COMMIT: ${YAMCS_GIT_COMMIT}
+ MISSION: ${MISSION}
+ SPACECRAFT: ${SPACECRAFT}
+ PROJECT_NAME: ${PROJECT_NAME}
+ networks:
+ mission-spacecraft-network:
+ aliases:
+ - active-gs
+ - yamcs
+ - cosmos
+ mission-network:
+
+ nos3-onair:
+ image: nos3-base-local:dev
+ env_file:
+ - ./.env
+ attach: true
+ container_name: ${COMPOSE_PROJECT_NAME}-onair
+ hostname: ${COMPOSE_PROJECT_NAME}-onair
+ working_dir: /home/nos3/builds/nos3/fsw/build/exe/cpu1
+ command: "/home/nos3/builds/nos3/scripts/fsw/onair_launch.sh"
+ volumes:
+ - ${PWD}/${NOS3_CFG_PATH}/cfg/build/sims/nos3-simulator.xml:/home/nos3/builds/nos3/sims/build/bin/nos3-simulator.xml
+ tty: true
+ stdin_open: true
+ privileged: true
+ environment:
+ BASE_DIR: /home/nos3/builds/nos3
+ FSW_DIR: /home/nos3/builds/nos3/fsw/build/exe/cpu1
+ HEALTHCHECK_PORT: ${HEALTHCHECK_PORT}
+ SCRIPT_DIR:
+ MISSION: ${MISSION}
+ SPACECRAFT: ${SPACECRAFT}
+ PROJECT_NAME: ${PROJECT_NAME}
+ networks:
+ mission-spacecraft-network:
+ aliases:
+ - onair
+ - sc01-onair
+ mission-network:
+
+ nos3-fsw:
+ image: nos3-base-local:dev
+ env_file:
+ - ./.env
+ build:
+ context: ./services/fsw
+ dockerfile: Dockerfile
+ args:
+
+ DEPLOYMENT_ENVIRO: ${DEPLOYMENT_ENVIRO}
+ HTTPS_PROXY: ${HTTPS_PROXY}
+ HTTP_PROXY: ${HTTP_PROXY}
+ NO_PROXY: ${NO_PROXY}
+ MAVEN_HTTPS_PROXY: ${MAVEN_HTTPS_PROXY}
+
+ GIT_URL: ${NOS3_GIT_URL}
+ GIT_COMMIT: ${NOS3_GIT_COMMIT}
+
+ NOS3_USER: ${NOS3_USER}
+ BASE_DIR: ${NOS3_BASE_DIR}
+ IMAGE_URI: ${NOS3_IMAGE_URI}
+ FSW_SOFTWARE: ${FSW_SOFTWARE}
+ GSW_SOFTWARE: ${GSW_SOFTWARE}
+ LD_LIBRARY_PATH: /usr/local/lib
+ GROUND_SOFTWARE: ${GSW_SOFTWARE}
+
+ healthcheck:
+ test: ["CMD-SHELL", "/usr/bin/pgrep core-cpu1"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
+ attach: true
+ depends_on:
+ nos3-fortytwo:
+ condition: service_healthy
+ nos3-nos-engine-server:
+ condition: service_healthy
+ container_name: ${COMPOSE_PROJECT_NAME}-fsw
+ hostname: ${COMPOSE_PROJECT_NAME}-fsw
+ working_dir: /home/nos3/builds/nos3/
+ volumes:
+ - ${PWD}/services/fsw/entrypoint.sh:/entrypoint.sh
+ command: "/entrypoint.sh"
+ sysctls:
+ fs.mqueue.msg_max: 10000
+ ulimits:
+ rtprio: 99
+ cap_add:
+ - sys_nice
+ environment:
+ BASE_DIR: /home/nos3/builds/nos3
+ FSW_DIR: /home/nos3/builds/nos3/fsw/build/exe/cpu1
+ HEALTHCHECK_PORT: ${HEALTHCHECK_PORT}
+ MISSION: ${MISSION}
+ SPACECRAFT: ${SPACECRAFT}
+ PROJECT_NAME: ${PROJECT_NAME}
+ networks:
+ mission-spacecraft-network:
+ aliases:
+ - fsw
+ - sc01-fsw
+ mission-network:
+
+ nos3-nos-engine-server:
+ image: nos3-base-local:dev
+ env_file:
+ - ./.env
+ attach: true
+ depends_on:
+ nos3-fortytwo:
+ condition: service_healthy
+ healthcheck:
+ test: ["CMD-SHELL", "/usr/bin/nc -vz nos-engine-server 12000 && /usr/bin/nc -vz nos-engine-server 12001"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
+ container_name: ${COMPOSE_PROJECT_NAME}-nos-engine-server
+ hostname: ${COMPOSE_PROJECT_NAME}-nos-engine-server
+ working_dir: /home/nos3/builds/nos3/sims/build/bin
+ command: "/usr/bin/nos_engine_server_standalone -f ./nos_engine_server_config.json"
+ volumes:
+ - ${PWD}/${NOS3_CFG_PATH}/cfg/build/sims/nos_engine_server_config.json:/home/nos3/builds/nos3/sims/build/bin/nos_engine_server_config.json
+ tty: true
+ environment:
+ HEALTHCHECK_PORT: ${HEALTHCHECK_PORT}
+ MISSION: ${MISSION}
+ SPACECRAFT: ${SPACECRAFT}
+ PROJECT_NAME: ${PROJECT_NAME}
+ networks:
+ mission-spacecraft-network:
+ aliases:
+# TODO: below is needed for some reason!
+ - sc01-nos-engine-server
+ - nos-engine-server
+ mission-network:
+
+ nos3-truth42sim:
+ image: nos3-base-local:dev
+ env_file:
+ - ./.env
+ attach: true
+ depends_on:
+ nos3-fortytwo:
+ condition: service_healthy
+ nos3-gsw:
+ condition: service_healthy
+ nos3-nos-engine-server:
+ condition: service_healthy
+ container_name: ${COMPOSE_PROJECT_NAME}-truth42sim
+ hostname: ${COMPOSE_PROJECT_NAME}-truth42sim
+ working_dir: /home/nos3/builds/nos3/sims/build/bin
+ command: "./nos3-single-simulator -f ./nos3-simulator.xml truth42sim"
+ volumes:
+ - ${PWD}/${NOS3_CFG_PATH}/cfg/build/sims/nos3-simulator.xml:/home/nos3/builds/nos3/sims/build/bin/nos3-simulator.xml
+ tty: true
+ stdin_open: true
+ privileged: true
+ environment:
+ HEALTHCHECK_PORT: ${HEALTHCHECK_PORT}
+ MISSION: ${MISSION}
+ SPACECRAFT: ${SPACECRAFT}
+ PROJECT_NAME: ${PROJECT_NAME}
+ networks:
+ mission-spacecraft-network:
+ aliases:
+ - truth42sim
+ mission-network:
+
+ nos3-nos-terminal:
+ image: nos3-base-local:dev
+ env_file:
+ - ./.env
+ attach: true
+ container_name: ${COMPOSE_PROJECT_NAME}-nos-terminal
+ hostname: ${COMPOSE_PROJECT_NAME}-nos-terminal
+ working_dir: /home/nos3/builds/nos3/sims/build/bin
+ command: "./nos3-single-simulator -f ./nos3-simulator.xml stdio-terminal"
+ volumes:
+ - ${PWD}/${NOS3_CFG_PATH}/cfg/build/sims/nos3-simulator.xml:/home/nos3/builds/nos3/sims/build/bin/nos3-simulator.xml
+ tty: true
+ stdin_open: true
+ privileged: true
+ environment:
+ HEALTHCHECK_PORT: ${HEALTHCHECK_PORT}
+ MISSION: ${MISSION}
+ SPACECRAFT: ${SPACECRAFT}
+ PROJECT_NAME: ${PROJECT_NAME}
+ networks:
+ mission-spacecraft-network:
+ aliases:
+ - nos-terminal
+ mission-network:
+
+ nos3-nos-udp-terminal:
+ image: nos3-base-local:dev
+ env_file:
+ - ./.env
+ attach: true
+ container_name: ${COMPOSE_PROJECT_NAME}-nos-udp-terminal
+ hostname: ${COMPOSE_PROJECT_NAME}-nos-udp-terminal
+ working_dir: /home/nos3/builds/nos3/sims/build/bin
+ command: "./nos3-single-simulator -f ./nos3-simulator.xml udp-terminal"
+ volumes:
+ - ${PWD}/${NOS3_CFG_PATH}/cfg/build/sims/nos3-simulator.xml:/home/nos3/builds/nos3/sims/build/bin/nos3-simulator.xml
+ tty: true
+ stdin_open: true
+ privileged: true
+ environment:
+ HEALTHCHECK_PORT: ${HEALTHCHECK_PORT}
+ MISSION: ${MISSION}
+ SPACECRAFT: ${SPACECRAFT}
+ PROJECT_NAME: ${PROJECT_NAME}
+ networks:
+ mission-spacecraft-network:
+ aliases:
+ - nos-udp-terminal
+ mission-network:
+
+ nos3-nos-sim-bridge:
+ image: nos3-base-local:dev
+ env_file:
+ - ./.env
+ attach: true
+ container_name: ${COMPOSE_PROJECT_NAME}-nos-sim-bridge
+ hostname: ${COMPOSE_PROJECT_NAME}-nos-sim-bridge
+ working_dir: /home/nos3/builds/nos3/sims/build/bin
+ command: "./nos3-sim-cmdbus-bridge -f ./nos3-simulator.xml"
+ volumes:
+ - ${PWD}/${NOS3_CFG_PATH}/cfg/build/sims/nos3-simulator.xml:/home/nos3/builds/nos3/sims/build/bin/nos3-simulator.xml
+ tty: true
+ stdin_open: true
+ privileged: true
+ environment:
+ HEALTHCHECK_PORT: ${HEALTHCHECK_PORT}
+ MISSION: ${MISSION}
+ SPACECRAFT: ${SPACECRAFT}
+ PROJECT_NAME: ${PROJECT_NAME}
+ networks:
+ mission-spacecraft-network:
+ aliases:
+ - nos-sim-bridge
+ mission-network:
+
+ nos3-camsim:
+ image: nos3-base-local:dev
+ env_file:
+ - ./.env
+ attach: true
+ depends_on:
+ nos3-fortytwo:
+ condition: service_healthy
+ nos3-fsw:
+ condition: service_healthy
+ container_name: ${COMPOSE_PROJECT_NAME}-cam-sim
+ hostname: ${COMPOSE_PROJECT_NAME}-cam-sim
+ working_dir: /home/nos3/builds/nos3/sims/build/bin
+ command: "./nos3-single-simulator -f ./nos3-simulator.xml camsim"
+ volumes:
+ - ${PWD}/${NOS3_CFG_PATH}/cfg/build/sims/nos3-simulator.xml:/home/nos3/builds/nos3/sims/build/bin/nos3-simulator.xml
+ environment:
+ HEALTHCHECK_PORT: ${HEALTHCHECK_PORT}
+ MISSION: ${MISSION}
+ SPACECRAFT: ${SPACECRAFT}
+ PROJECT_NAME: ${PROJECT_NAME}
+ networks:
+ mission-spacecraft-network:
+ aliases:
+ - camsim
+# TODO: needed?
+ - sc01-camsim
+ mission-network:
+
+ nos3-css-sim:
+ image: nos3-base-local:dev
+ env_file:
+ - ./.env
+ attach: true
+ depends_on:
+ nos3-fortytwo:
+ condition: service_healthy
+ nos3-fsw:
+ condition: service_healthy
+ container_name: ${COMPOSE_PROJECT_NAME}-css-sim
+ hostname: ${COMPOSE_PROJECT_NAME}-css-sim
+ working_dir: /home/nos3/builds/nos3/sims/build/bin
+ command: "./nos3-single-simulator -f ./nos3-simulator.xml generic-css-sim"
+ volumes:
+ - ${PWD}/${NOS3_CFG_PATH}/cfg/build/sims/nos3-simulator.xml:/home/nos3/builds/nos3/sims/build/bin/nos3-simulator.xml
+ environment:
+ HEALTHCHECK_PORT: ${HEALTHCHECK_PORT}
+ MISSION: ${MISSION}
+ SPACECRAFT: ${SPACECRAFT}
+ PROJECT_NAME: ${PROJECT_NAME}
+ networks:
+ mission-spacecraft-network:
+ aliases:
+ - css
+ - css-sim
+ mission-network:
+
+ nos3-eps-sim:
+ image: nos3-base-local:dev
+ env_file:
+ - ./.env
+ attach: true
+ depends_on:
+ nos3-nos-engine-server:
+ condition: service_healthy
+ nos3-fortytwo:
+ condition: service_healthy
+ nos3-fsw:
+ condition: service_healthy
+ container_name: ${COMPOSE_PROJECT_NAME}-eps-sim
+ hostname: ${COMPOSE_PROJECT_NAME}-eps-sim
+ working_dir: /home/nos3/builds/nos3/sims/build/bin
+ command: "./nos3-single-simulator -f ./nos3-simulator.xml generic-eps-sim"
+ volumes:
+ - ${PWD}/${NOS3_CFG_PATH}/cfg/build/sims/nos3-simulator.xml:/home/nos3/builds/nos3/sims/build/bin/nos3-simulator.xml
+ environment:
+ HEALTHCHECK_PORT: ${HEALTHCHECK_PORT}
+ MISSION: ${MISSION}
+ SPACECRAFT: ${SPACECRAFT}
+ PROJECT_NAME: ${PROJECT_NAME}
+ networks:
+ mission-spacecraft-network:
+ aliases:
+ - eps
+# TODO: needed?
+ - sc01-eps-sim
+ mission-network:
+
+ nos3-fss-sim:
+ image: nos3-base-local:dev
+ env_file:
+ - ./.env
+ attach: true
+ depends_on:
+ nos3-fortytwo:
+ condition: service_healthy
+ nos3-fsw:
+ condition: service_healthy
+ container_name: ${COMPOSE_PROJECT_NAME}-fss-sim
+ hostname: ${COMPOSE_PROJECT_NAME}-fss-sim
+ working_dir: /home/nos3/builds/nos3/sims/build/bin
+ command: "./nos3-single-simulator -f ./nos3-simulator.xml generic-fss-sim"
+ volumes:
+ - ${PWD}/${NOS3_CFG_PATH}/cfg/build/sims/nos3-simulator.xml:/home/nos3/builds/nos3/sims/build/bin/nos3-simulator.xml
+ environment:
+ HEALTHCHECK_PORT: ${HEALTHCHECK_PORT}
+ MISSION: ${MISSION}
+ SPACECRAFT: ${SPACECRAFT}
+ PROJECT_NAME: ${PROJECT_NAME}
+ networks:
+ mission-spacecraft-network:
+ aliases:
+ - fss
+# TODO: needed?
+ - sc01-fss-sim
+ mission-network:
+
+ nos3-gps-sim:
+ image: nos3-base-local:dev
+ env_file:
+ - ./.env
+ attach: true
+ depends_on:
+ nos3-fortytwo:
+ condition: service_healthy
+ nos3-fsw:
+ condition: service_healthy
+ container_name: ${COMPOSE_PROJECT_NAME}-gps-sim
+ hostname: ${COMPOSE_PROJECT_NAME}-gps-sim
+ working_dir: /home/nos3/builds/nos3/sims/build/bin
+ command: "./nos3-single-simulator -f ./nos3-simulator.xml gps"
+ volumes:
+ - ${PWD}/${NOS3_CFG_PATH}/cfg/build/sims/nos3-simulator.xml:/home/nos3/builds/nos3/sims/build/bin/nos3-simulator.xml
+ environment:
+ HEALTHCHECK_PORT: ${HEALTHCHECK_PORT}
+ MISSION: ${MISSION}
+ SPACECRAFT: ${SPACECRAFT}
+ PROJECT_NAME: ${PROJECT_NAME}
+ networks:
+ mission-spacecraft-network:
+ aliases:
+ - gps
+# TODO: needed?
+ - sc01-gps-sims
+ mission-network:
+
+ nos3-imu-sim:
+ image: nos3-base-local:dev
+ env_file:
+ - ./.env
+ attach: true
+ depends_on:
+ nos3-fortytwo:
+ condition: service_healthy
+ nos3-fsw:
+ condition: service_healthy
+ container_name: ${COMPOSE_PROJECT_NAME}-imu-sim
+ hostname: ${COMPOSE_PROJECT_NAME}-imu-sim
+ working_dir: /home/nos3/builds/nos3/sims/build/bin
+ command: "./nos3-single-simulator -f ./nos3-simulator.xml generic-imu-sim"
+ volumes:
+ - ${PWD}/${NOS3_CFG_PATH}/cfg/build/sims/nos3-simulator.xml:/home/nos3/builds/nos3/sims/build/bin/nos3-simulator.xml
+ environment:
+ HEALTHCHECK_PORT: ${HEALTHCHECK_PORT}
+ MISSION: ${MISSION}
+ SPACECRAFT: ${SPACECRAFT}
+ PROJECT_NAME: ${PROJECT_NAME}
+ networks:
+ mission-spacecraft-network:
+ aliases:
+ - imu
+# TODO: needed?
+ - sc01-imu-sim
+ mission-network:
+
+ nos3-mag-sim:
+ image: nos3-base-local:dev
+ env_file:
+ - ./.env
+ attach: true
+ depends_on:
+ nos3-fortytwo:
+ condition: service_healthy
+ nos3-fsw:
+ condition: service_healthy
+ container_name: ${COMPOSE_PROJECT_NAME}-mag-sim
+ hostname: ${COMPOSE_PROJECT_NAME}-mag-sim
+ working_dir: /home/nos3/builds/nos3/sims/build/bin
+ command: "./nos3-single-simulator -f ./nos3-simulator.xml generic-mag-sim"
+ volumes:
+ - ${PWD}/${NOS3_CFG_PATH}/cfg/build/sims/nos3-simulator.xml:/home/nos3/builds/nos3/sims/build/bin/nos3-simulator.xml
+ environment:
+ HEALTHCHECK_PORT: ${HEALTHCHECK_PORT}
+ MISSION: ${MISSION}
+ SPACECRAFT: ${SPACECRAFT}
+ PROJECT_NAME: ${PROJECT_NAME}
+ networks:
+ mission-spacecraft-network:
+ aliases:
+ - mag
+# TODO: needed?
+ - sc01-mag-sim
+ mission-network:
+
+ nos3-rw-sim0:
+ image: nos3-base-local:dev
+ env_file:
+ - ./.env
+ attach: true
+ depends_on:
+ nos3-fortytwo:
+ condition: service_healthy
+ nos3-fsw:
+ condition: service_healthy
+ container_name: ${COMPOSE_PROJECT_NAME}-rw-sim0
+ hostname: ${COMPOSE_PROJECT_NAME}-rw-sim0
+ working_dir: /home/nos3/builds/nos3/sims/build/bin
+ command: "./nos3-single-simulator -f ./nos3-simulator.xml generic-reactionwheel-sim0"
+ volumes:
+ - ${PWD}/${NOS3_CFG_PATH}/cfg/build/sims/nos3-simulator.xml:/home/nos3/builds/nos3/sims/build/bin/nos3-simulator.xml
+ environment:
+ HEALTHCHECK_PORT: ${HEALTHCHECK_PORT}
+ MISSION: ${MISSION}
+ SPACECRAFT: ${SPACECRAFT}
+ PROJECT_NAME: ${PROJECT_NAME}
+ networks:
+ mission-spacecraft-network:
+ aliases:
+ - rw0
+# TODO: needed?
+ - sc01-rw-sim0
+ mission-network:
+
+ nos3-rw-sim1:
+ image: nos3-base-local:dev
+ env_file:
+ - ./.env
+ attach: true
+ depends_on:
+ nos3-fortytwo:
+ condition: service_healthy
+ nos3-fsw:
+ condition: service_healthy
+ container_name: ${COMPOSE_PROJECT_NAME}-rw-sim1
+ hostname: ${COMPOSE_PROJECT_NAME}-rw-sim1
+ working_dir: /home/nos3/builds/nos3/sims/build/bin
+ command: "./nos3-single-simulator -f ./nos3-simulator.xml generic-reactionwheel-sim1"
+ volumes:
+ - ${PWD}/${NOS3_CFG_PATH}/cfg/build/sims/nos3-simulator.xml:/home/nos3/builds/nos3/sims/build/bin/nos3-simulator.xml
+ environment:
+ HEALTHCHECK_PORT: ${HEALTHCHECK_PORT}
+ MISSION: ${MISSION}
+ SPACECRAFT: ${SPACECRAFT}
+ PROJECT_NAME: ${PROJECT_NAME}
+ networks:
+ mission-spacecraft-network:
+ aliases:
+ - rw1
+# TODO: needed?
+ - sc01-rw-sim1
+ mission-network:
+
+ nos3-rw-sim2:
+ image: nos3-base-local:dev
+ env_file:
+ - ./.env
+ attach: true
+ depends_on:
+ nos3-fortytwo:
+ condition: service_healthy
+ nos3-fsw:
+ condition: service_healthy
+ container_name: ${COMPOSE_PROJECT_NAME}-rw-sim2
+ hostname: ${COMPOSE_PROJECT_NAME}-rw-sim2
+ working_dir: /home/nos3/builds/nos3/sims/build/bin
+ command: "./nos3-single-simulator -f ./nos3-simulator.xml generic-reactionwheel-sim2"
+ volumes:
+ - ${PWD}/${NOS3_CFG_PATH}/cfg/build/sims/nos3-simulator.xml:/home/nos3/builds/nos3/sims/build/bin/nos3-simulator.xml
+ environment:
+ HEALTHCHECK_PORT: ${HEALTHCHECK_PORT}
+ MISSION: ${MISSION}
+ SPACECRAFT: ${SPACECRAFT}
+ PROJECT_NAME: ${PROJECT_NAME}
+ networks:
+ mission-spacecraft-network:
+ aliases:
+ - rw2
+# TODO: needed?
+ - sc01-rw-sim2
+ mission-network:
+
+ # nos3-radio-sim:
+ # image: ghcr.io/haisamido/nos3-base:dev
+ # env_file:
+ # - ./.env
+ # attach: true
+ # healthcheck:
+ # test: [ "CMD-SHELL", "/usr/bin/pgrep -f nos3-single-simulator" ]
+ # interval: 10s
+ # timeout: 5s
+ # retries: 7
+ # depends_on:
+ # nos3-cryptolib:
+ # condition: service_started
+ # container_name: sc01-radio-sim
+ # hostname: radio-sim
+ # working_dir: /home/nos3/builds/nos3/sims/build/bin
+ # command: "/entrypoint.sh"
+ # volumes:
+ # - ${PWD}/${NOS3_CFG_PATH}/cfg/build/sims/nos3-simulator.xml:/home/nos3/builds/nos3/sims/build/bin/nos3-simulator.xml
+ # - ${PWD}/radio-sim/entrypoint.sh:/entrypoint.sh
+ # environment:
+ # HEALTHCHECK_PORT: ${HEALTHCHECK_PORT}
+ # networks:
+ # mission-spacecraft-network:
+ # # aliases:
+ # # - radio-sim
+ # mission-network:
+
+# nos3-cryptolib:
+# image: nos3-base-local:dev
+# env_file:
+# - ./.env
+# attach: true
+# healthcheck:
+# test: ["CMD-SHELL", "/usr/bin/pgrep -f ./support/standalone"]
+# interval: 10s
+# timeout: 5s
+# retries: 5
+# depends_on:
+# # nos3-radio-sim:
+# # condition: service_started
+# nos3-fsw:
+# condition: service_healthy
+# container_name: sc01_cryptolib-gsw
+# # hostname: cryptolib
+# working_dir: /home/nos3/builds/nos3/gsw/build
+# command: "./support/standalone"
+# volumes:
+# - ${PWD}/${NOS3_CFG_PATH}/cfg/build/sims/nos3-simulator.xml:/home/nos3/builds/nos3/sims/build/bin/nos3-simulator.xml
+# environment:
+# HEALTHCHECK_PORT: ${HEALTHCHECK_PORT}
+# networks:
+# mission-spacecraft-network:
+# aliases:
+# - cryptolib
+# mission-network:
+
+ nos3-radio-sim-cryptolib:
+ image: nos3-base-local:dev
+ env_file:
+ - ./.env
+ attach: true
+ healthcheck:
+ test: [ "CMD-SHELL", "/usr/bin/pgrep -f nos3-single-simulator && /usr/bin/pgrep -f ./support/standalone" ]
+ interval: 10s
+ timeout: 5s
+ retries: 7
+ depends_on:
+ nos3-fsw:
+ condition: service_healthy
+ nos3-gsw:
+ condition: service_healthy
+ container_name: ${COMPOSE_PROJECT_NAME}-radio-sim-cryptolib
+ hostname: ${COMPOSE_PROJECT_NAME}- radio-sim-cryptolib
+ working_dir: /home/nos3/builds/nos3/sims/build/bin
+ command: "/entrypoint.sh"
+ volumes:
+ - ${PWD}/${NOS3_CFG_PATH}/cfg/build/sims/nos3-simulator.xml:/home/nos3/builds/nos3/sims/build/bin/nos3-simulator.xml
+ - ${PWD}/services/radio-sim/entrypoint2.sh:/entrypoint.sh
+ environment:
+ HEALTHCHECK_PORT: ${HEALTHCHECK_PORT}
+ MISSION: ${MISSION}
+ SPACECRAFT: ${SPACECRAFT}
+ PROJECT_NAME: ${PROJECT_NAME}
+ networks:
+ mission-spacecraft-network:
+ aliases:
+ - radio-sim
+ - cryptolib
+ mission-network:
+
+ nos3-sample-sim:
+ image: nos3-base-local:dev
+ env_file:
+ - ./.env
+ attach: true
+ depends_on:
+ nos3-fortytwo:
+ condition: service_healthy
+ nos3-fsw:
+ condition: service_healthy
+ container_name: ${COMPOSE_PROJECT_NAME}-sample-sim
+ hostname: ${COMPOSE_PROJECT_NAME}-sample-sim
+ working_dir: /home/nos3/builds/nos3/sims/build/bin
+ command: "./nos3-single-simulator -f ./nos3-simulator.xml sample-sim"
+ volumes:
+ - ${PWD}/${NOS3_CFG_PATH}/cfg/build/sims/nos3-simulator.xml:/home/nos3/builds/nos3/sims/build/bin/nos3-simulator.xml
+ environment:
+ HEALTHCHECK_PORT: ${HEALTHCHECK_PORT}
+ MISSION: ${MISSION}
+ SPACECRAFT: ${SPACECRAFT}
+ PROJECT_NAME: ${PROJECT_NAME}
+ networks:
+ mission-spacecraft-network:
+ aliases:
+ - sample-sim
+ mission-network:
+
+ nos3-startrk-sim:
+ image: nos3-base-local:dev
+ env_file:
+ - ./.env
+ attach: true
+ depends_on:
+ nos3-fortytwo:
+ condition: service_healthy
+ nos3-fsw:
+ condition: service_healthy
+ container_name: ${COMPOSE_PROJECT_NAME}-startrk-sim
+ hostname: ${COMPOSE_PROJECT_NAME}-startrk-sim
+ working_dir: /home/nos3/builds/nos3/sims/build/bin
+ command: "./nos3-single-simulator -f ./nos3-simulator.xml generic-star-tracker-sim"
+ volumes:
+ - ${PWD}/${NOS3_CFG_PATH}/cfg/build/sims/nos3-simulator.xml:/home/nos3/builds/nos3/sims/build/bin/nos3-simulator.xml
+ environment:
+ HEALTHCHECK_PORT: ${HEALTHCHECK_PORT}
+ MISSION: ${MISSION}
+ SPACECRAFT: ${SPACECRAFT}
+ PROJECT_NAME: ${PROJECT_NAME}
+ networks:
+ mission-spacecraft-network:
+ aliases:
+ - startrk-sim
+ mission-network:
+
+ nos3-thruster-sim:
+ image: nos3-base-local:dev
+ env_file:
+ - ./.env
+ attach: true
+ depends_on:
+ nos3-fortytwo:
+ condition: service_healthy
+ nos3-fsw:
+ condition: service_healthy
+ container_name: ${COMPOSE_PROJECT_NAME}-thruster-sim
+ hostname: ${COMPOSE_PROJECT_NAME}-thruster-sim
+ working_dir: /home/nos3/builds/nos3/sims/build/bin
+ command: "./nos3-single-simulator -f ./nos3-simulator.xml generic-thruster-sim"
+ volumes:
+ - ${PWD}/${NOS3_CFG_PATH}/cfg/build/sims/nos3-simulator.xml:/home/nos3/builds/nos3/sims/build/bin/nos3-simulator.xml
+ environment:
+ HEALTHCHECK_PORT: ${HEALTHCHECK_PORT}
+ MISSION: ${MISSION}
+ SPACECRAFT: ${SPACECRAFT}
+ PROJECT_NAME: ${PROJECT_NAME}
+ networks:
+ mission-spacecraft-network:
+ aliases:
+ - thruster-sim
+ mission-network:
+
+ nos3-torquer-sim:
+ image: nos3-base-local:dev
+ env_file:
+ - ./.env
+ attach: true
+ depends_on:
+ nos3-fortytwo:
+ condition: service_healthy
+ nos3-fsw:
+ condition: service_healthy
+ container_name: ${COMPOSE_PROJECT_NAME}-torquer-sim
+ hostname: ${COMPOSE_PROJECT_NAME}-torquer-sim
+ working_dir: /home/nos3/builds/nos3/sims/build/bin
+ command: "./nos3-single-simulator -f ./nos3-simulator.xml generic-torquer-sim"
+ volumes:
+ - ${PWD}/${NOS3_CFG_PATH}/cfg/build/sims/nos3-simulator.xml:/home/nos3/builds/nos3/sims/build/bin/nos3-simulator.xml
+ environment:
+ HEALTHCHECK_PORT: ${HEALTHCHECK_PORT}
+ MISSION: ${MISSION}
+ SPACECRAFT: ${SPACECRAFT}
+ PROJECT_NAME: ${PROJECT_NAME}
+ networks:
+ mission-spacecraft-network:
+ aliases:
+ - torquer-sim
+ - trq-sim
+ mission-network:
+
+ nos3-time:
+ image: nos3-base-local:dev
+ env_file:
+ - ./.env
+ attach: true
+ depends_on:
+ nos3-nos-engine-server:
+ condition: service_healthy
+ container_name: ${COMPOSE_PROJECT_NAME}-nos-time-driver
+ hostname: ${COMPOSE_PROJECT_NAME}-nos-time-driver
+ working_dir: /home/nos3/builds/nos3/sims/build/bin
+ command: "./nos3-single-simulator -f ./nos3-simulator.xml time"
+ volumes:
+ - ${PWD}/${NOS3_CFG_PATH}/cfg/build/sims/nos3-simulator.xml:/home/nos3/builds/nos3/sims/build/bin/nos3-simulator.xml
+ tty: true
+ stdin_open: true
+ privileged: true
+ environment:
+ HEALTHCHECK_PORT: ${HEALTHCHECK_PORT}
+ MISSION: ${MISSION}
+ SPACECRAFT: ${SPACECRAFT}
+ PROJECT_NAME: ${PROJECT_NAME}
+ networks:
+ mission-spacecraft-network:
+ aliases:
+ - ${COMPOSE_PROJECT_NAME}-nos-time-driver
+ mission-network:
+
+ nos3-openmct:
+ env_file:
+ - ./.env
+ build:
+ context: ./services/openmct
+ dockerfile: Containerfile
+ args:
+ DEPLOYMENT_ENVIRO: ${DEPLOYMENT_ENVIRO}
+ HTTPS_PROXY: ${HTTPS_PROXY}
+ HTTP_PROXY: ${HTTP_PROXY}
+ NO_PROXY: ${NO_PROXY}
+
+ IMAGE_URI: ${OPENMCT_IMAGE_URI}
+ NVM_VERSION: ${OPENMCT_NVM_VERSION}
+ NODE_VERSION: ${OPENMCT_NODE_VERSION}
+
+ GIT_URL: ${OPENMCT_GIT_URL}
+ GIT_COMMIT: ${OPENMCT_GIT_COMMIT}
+
+ attach: true
+ container_name: ${COMPOSE_PROJECT_NAME}-openmct
+ hostname: ${COMPOSE_PROJECT_NAME}-openmct
+ working_dir: "/opt/openmct"
+ volumes:
+ - "${PWD}/services/openmct/entrypoint.sh:/opt/openmct/entrypoint.sh"
+ - "${PWD}/services/openmct/webpack.dev.mjs:/opt/openmct/.webpack/webpack.dev.mjs"
+ - "${PWD}/services/openmct/example/index.js:/opt/openmct/example/index.js"
+ - "${PWD}/services/openmct/example/make-example-events.mjs:/opt/openmct/example/make-example-events.mjs"
+ command: '/opt/openmct/entrypoint.sh'
+ healthcheck:
+ test: ["CMD-SHELL", "curl http://localhost:${OPENMCT_PORT}/"]
+ interval: 10s
+ timeout: 5s
+ retries: 30
+ start_period: 30s
+ depends_on:
+ nos3-gsw:
+ condition: service_healthy
+ ports:
+ - "127.0.0.1:${OPENMCT_HOST_PORT}:${OPENMCT_PORT}"
+ environment:
+ MISSION: ${MISSION}
+ SPACECRAFT: ${SPACECRAFT}
+ PROJECT_NAME: ${PROJECT_NAME}
+ networks:
+ mission-spacecraft-network:
+ aliases:
+ - openmct
+ mission-network:
+ fleet-network:
+
+networks:
+ fleet-network:
+ external: ${EXTERNAL_NETWORK_FLEET:-false}
+ name: ${FLEET}-fleet
+ mission-network:
+ external: ${EXTERNAL_NETWORK_MISSION:-false}
+ name: ${PROJECT_MISSION}
+ mission-spacecraft-network:
+ external: ${EXTERNAL_NETWORK_MISSION_SPACECRAFT:-false}
+ name: ${COMPOSE_PROJECT_NAME}
diff --git a/deployments/docs/FlowDiagram.md b/deployments/docs/FlowDiagram.md
new file mode 100644
index 000000000..082a8c85b
--- /dev/null
+++ b/deployments/docs/FlowDiagram.md
@@ -0,0 +1,212 @@
+# NOS3 Data Flow Diagram
+
+```mermaid
+flowchart TB
+ subgraph External["External Access"]
+ User["👤 User/Browser"]
+ end
+
+ subgraph GroundSoftware["Ground Software"]
+ YAMCS["nos3-gsw
(YAMCS)
:8090, :5012"]
+ OpenMCT["nos3-openmct
:9000, :8080"]
+ end
+
+ subgraph FlightSoftware["Flight Software"]
+ FSW["nos3-fsw
(cFS Flight Software)"]
+ OnAir["nos3-onair
(On-Air Launch)"]
+ end
+
+ subgraph SimulationCore["Simulation Core"]
+ FortyTwo["nos3-fortytwo
(42 Simulator)
:30090"]
+ NosEngine["nos3-nos-engine-server
:12000, :12001"]
+ Time["nos3-time
(Time Driver)"]
+ Truth42["nos3-truth42sim"]
+ end
+
+ subgraph Terminals["Terminals & Bridges"]
+ Terminal["nos3-nos-terminal
(STDIO Terminal)"]
+ UDPTerminal["nos3-nos-udp-terminal
(UDP Terminal)"]
+ SimBridge["nos3-nos-sim-bridge
(Command Bus Bridge)"]
+ end
+
+ subgraph SensorSimulators["Sensor Simulators"]
+ CSS["nos3-css-sim
(Coarse Sun Sensor)"]
+ FSS["nos3-fss-sim
(Fine Sun Sensor)"]
+ GPS["nos3-gps-sim"]
+ IMU["nos3-imu-sim"]
+ MAG["nos3-mag-sim
(Magnetometer)"]
+ StarTrk["nos3-startrk-sim
(Star Tracker)"]
+ CAM["nos3-camsim
(Camera)"]
+ end
+
+ subgraph ActuatorSimulators["Actuator Simulators"]
+ RW0["nos3-rw-sim0
(Reaction Wheel 0)"]
+ RW1["nos3-rw-sim1
(Reaction Wheel 1)"]
+ RW2["nos3-rw-sim2
(Reaction Wheel 2)"]
+ Thruster["nos3-thruster-sim"]
+ Torquer["nos3-torquer-sim"]
+ end
+
+ subgraph OtherSims["Other Simulators"]
+ EPS["nos3-eps-sim
(Power System)"]
+ Sample["nos3-sample-sim"]
+ RadioCrypto["nos3-radio-sim-cryptolib
(Radio + CryptoLib)"]
+ end
+
+ %% User Access
+ User -->|":8090 YAMCS Web"| YAMCS
+ User -->|":9000/:8080 OpenMCT"| OpenMCT
+ User -->|":30090 42 VNC"| FortyTwo
+
+ %% Ground to Flight Communication
+ OpenMCT -->|"Telemetry API"| YAMCS
+ YAMCS <-->|"Commands/Telemetry
via RadioCrypto"| RadioCrypto
+ RadioCrypto <-->|"Encrypted Comms"| FSW
+
+ %% Core Dependencies
+ FSW -->|"depends_on"| FortyTwo
+ FSW -->|"depends_on"| NosEngine
+ Time -->|"depends_on"| NosEngine
+ Truth42 -->|"depends_on"| NosEngine
+
+ %% NOS Engine as central hub
+ NosEngine <-->|"Time Sync"| Time
+ NosEngine <-->|"Simulation Bus"| FSW
+ NosEngine <-->|"42 Data"| Truth42
+
+ %% 42 Simulator provides environment data
+ FortyTwo -->|"Spacecraft State
(Position, Attitude)"| Truth42
+ FortyTwo -->|"Sun Vector"| CSS
+ FortyTwo -->|"Sun Vector"| FSS
+ FortyTwo -->|"Orbit/Position"| GPS
+ FortyTwo -->|"Angular Rates"| IMU
+ FortyTwo -->|"Magnetic Field"| MAG
+ FortyTwo -->|"Star Field"| StarTrk
+ FortyTwo -->|"Scene Data"| CAM
+ FortyTwo -->|"Solar Power"| EPS
+
+ %% Sensor data to FSW
+ CSS -->|"Sensor Data"| FSW
+ FSS -->|"Sensor Data"| FSW
+ GPS -->|"Sensor Data"| FSW
+ IMU -->|"Sensor Data"| FSW
+ MAG -->|"Sensor Data"| FSW
+ StarTrk -->|"Sensor Data"| FSW
+ CAM -->|"Sensor Data"| FSW
+ EPS -->|"Power Status"| FSW
+ Sample -->|"Sample Data"| FSW
+
+ %% FSW commands to actuators
+ FSW -->|"Wheel Commands"| RW0
+ FSW -->|"Wheel Commands"| RW1
+ FSW -->|"Wheel Commands"| RW2
+ FSW -->|"Thrust Commands"| Thruster
+ FSW -->|"Torque Commands"| Torquer
+
+ %% Actuator feedback to 42
+ RW0 -->|"Torque Applied"| FortyTwo
+ RW1 -->|"Torque Applied"| FortyTwo
+ RW2 -->|"Torque Applied"| FortyTwo
+ Thruster -->|"Thrust Applied"| FortyTwo
+ Torquer -->|"Torque Applied"| FortyTwo
+
+ %% Terminals
+ SimBridge <-->|"Command Bus"| FSW
+ Terminal <-->|"Debug I/O"| NosEngine
+ UDPTerminal <-->|"UDP Debug"| NosEngine
+
+ %% Styling
+ classDef ground fill:#4a90d9,stroke:#2c5282,color:#fff
+ classDef flight fill:#48bb78,stroke:#276749,color:#fff
+ classDef simcore fill:#ed8936,stroke:#c05621,color:#fff
+ classDef sensor fill:#9f7aea,stroke:#6b46c1,color:#fff
+ classDef actuator fill:#f56565,stroke:#c53030,color:#fff
+ classDef terminal fill:#718096,stroke:#4a5568,color:#fff
+ classDef other fill:#38b2ac,stroke:#285e61,color:#fff
+ classDef external fill:#ecc94b,stroke:#b7791f,color:#000
+
+ class YAMCS,OpenMCT ground
+ class FSW,OnAir flight
+ class FortyTwo,NosEngine,Time,Truth42 simcore
+ class CSS,FSS,GPS,IMU,MAG,StarTrk,CAM sensor
+ class RW0,RW1,RW2,Thruster,Torquer actuator
+ class Terminal,UDPTerminal,SimBridge terminal
+ class EPS,Sample,RadioCrypto other
+ class User external
+```
+
+## NOS3 Architecture Overview
+
+The diagram shows the **NASA Operational Simulator for Small Satellites (NOS3)** system with these key data flows:
+
+### Core Components
+
+| Component | Purpose | Ports |
+|-----------|---------|-------|
+| **nos3-fortytwo** | 42 Spacecraft Dynamics Simulator | :30090 (VNC) |
+| **nos3-nos-engine-server** | Central simulation bus | :12000, :12001 |
+| **nos3-fsw** | cFS Flight Software | - |
+| **nos3-gsw (YAMCS)** | Ground Software/Mission Control | :8090, :5012 |
+| **nos3-openmct** | Web-based telemetry visualization | :9000, :8080 |
+
+### Data Flow Patterns
+
+1. **Environment → Sensors**: 42 Simulator provides spacecraft state data (sun vectors, magnetic field, star field, etc.) to sensor simulators
+
+2. **Sensors → FSW**: Sensor simulators send telemetry to the Flight Software
+
+3. **FSW → Actuators**: Flight Software sends commands to reaction wheels, thrusters, and torquers
+
+4. **Actuators → Environment**: Actuator responses feed back to 42 Simulator to update spacecraft dynamics
+
+5. **FSW ↔ Ground**: Radio simulator with CryptoLib handles encrypted communication between flight and ground software
+
+### Networks
+
+All services connect to both Docker networks:
+- **nos3-core**: Core infrastructure network
+- **nos3-sc01**: Spacecraft 01 network
+
+### Service Dependencies
+
+```mermaid
+flowchart LR
+ subgraph StartupOrder["Startup Order"]
+ NE["nos-engine-server"] --> FSW["nos3-fsw"]
+ FT["nos3-fortytwo"] --> FSW
+ FSW --> Sims["Sensor/Actuator Sims"]
+ FSW --> Radio["radio-sim-cryptolib"]
+ YAMCS["nos3-gsw"] --> Radio
+ YAMCS --> OpenMCT["nos3-openmct"]
+ end
+```
+
+### Sensor Simulators
+
+| Simulator | Description |
+|-----------|-------------|
+| **nos3-css-sim** | Coarse Sun Sensor |
+| **nos3-fss-sim** | Fine Sun Sensor |
+| **nos3-gps-sim** | GPS Receiver |
+| **nos3-imu-sim** | Inertial Measurement Unit |
+| **nos3-mag-sim** | Magnetometer |
+| **nos3-startrk-sim** | Star Tracker |
+| **nos3-camsim** | Camera |
+
+### Actuator Simulators
+
+| Simulator | Description |
+|-----------|-------------|
+| **nos3-rw-sim0/1/2** | Reaction Wheels (3-axis control) |
+| **nos3-thruster-sim** | Thruster |
+| **nos3-torquer-sim** | Magnetic Torquer |
+
+### Other Simulators
+
+| Simulator | Description |
+|-----------|-------------|
+| **nos3-eps-sim** | Electrical Power System |
+| **nos3-sample-sim** | Sample/Payload Simulator |
+| **nos3-radio-sim-cryptolib** | Radio with encryption (CryptoLib) |
+| **nos3-time** | Time synchronization driver |
+| **nos3-truth42sim** | Ground truth from 42 |
diff --git a/deployments/kustomization.yaml b/deployments/kustomization.yaml
new file mode 100644
index 000000000..ca3f98015
--- /dev/null
+++ b/deployments/kustomization.yaml
@@ -0,0 +1,5 @@
+---
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+resources:
+ - ./services/fortytwo/kubernetes/
diff --git a/deployments/nos3/cfg/InOut/Inp_IPC.txt b/deployments/nos3/cfg/InOut/Inp_IPC.txt
new file mode 100644
index 000000000..fe1505fcd
--- /dev/null
+++ b/deployments/nos3/cfg/InOut/Inp_IPC.txt
@@ -0,0 +1,21 @@
+<<<<<<<<<<<<<<< 42: InterProcess Comm Configuration File >>>>>>>>>>>>>>>>
+2 ! Number of Sockets
+********************************** GPS IPC *****************************
+TX ! IPC Mode (OFF,TX,RX,TXRX,ACS,WRITEFILE,READFILE)
+"State03.42" ! File name for WRITE or READ
+SERVER ! Socket Role (SERVER,CLIENT,GMSEC_CLIENT)
+fortytwo 4245 ! Server Host Name, Port
+FALSE ! Allow Blocking (i.e. wait on RX)
+FALSE ! Echo to stdout
+1 ! Number of TX prefixes
+"SC[0].GPS[0]" ! Prefix 0
+********************************** Truth data to sim to pass to COSMOS ********************
+TX ! IPC Mode (OFF,TX,RX,TXRX,ACS,WRITEFILE,READFILE)
+"State.42" ! File name for WRITE or READ
+SERVER ! Socket Role (SERVER,CLIENT,GMSEC_CLIENT)
+fortytwo 9999 ! Server Host Name, Port
+FALSE ! Allow Blocking (i.e. wait on RX)
+FALSE ! Echo to stdout
+2 ! Number of TX prefixes
+"SC" ! Prefix 0
+"Orb" ! Prefix 1
diff --git a/deployments/nos3/cfg/InOut/Inp_IPC.txt.orig b/deployments/nos3/cfg/InOut/Inp_IPC.txt.orig
new file mode 100644
index 000000000..6dcb7b493
--- /dev/null
+++ b/deployments/nos3/cfg/InOut/Inp_IPC.txt.orig
@@ -0,0 +1,161 @@
+<<<<<<<<<<<<<<< 42: InterProcess Comm Configuration File >>>>>>>>>>>>>>>>
+17 ! Number of Sockets
+********************************** RW 0 to 42 *****************************
+RX ! IPC Mode (OFF,TX,RX,TXRX,ACS,WRITEFILE,READFILE)
+"State01.42" ! File name for WRITE or READ
+SERVER ! Socket Role (SERVER,CLIENT,GMSEC_CLIENT)
+fortytwo 4278 ! Server Host Name, Port
+FALSE ! Allow Blocking (i.e. wait on RX)
+FALSE ! Echo to stdout
+1 ! Number of TX prefixes
+"SC" ! Prefix 0
+********************************** RW 0 from 42 *****************************
+TX ! IPC Mode (OFF,TX,RX,TXRX,ACS,WRITEFILE,READFILE)
+"State02.42" ! File name for WRITE or READ
+SERVER ! Socket Role (SERVER,CLIENT,GMSEC_CLIENT)
+fortytwo 4277 ! Server Host Name, Port
+FALSE ! Allow Blocking (i.e. wait on RX)
+FALSE ! Echo to stdout
+1 ! Number of TX prefixes
+"SC[0].Whl[0].H" ! Prefix 0
+********************************** RW 1 to 42 *****************************
+RX ! IPC Mode (OFF,TX,RX,TXRX,ACS,WRITEFILE,READFILE)
+"State01.42" ! File name for WRITE or READ
+SERVER ! Socket Role (SERVER,CLIENT,GMSEC_CLIENT)
+fortytwo 4378 ! Server Host Name, Port
+FALSE ! Allow Blocking (i.e. wait on RX)
+FALSE ! Echo to stdout
+1 ! Number of TX prefixes
+"SC" ! Prefix 0
+********************************** RW 1 from 42 *****************************
+TX ! IPC Mode (OFF,TX,RX,TXRX,ACS,WRITEFILE,READFILE)
+"State02.42" ! File name for WRITE or READ
+SERVER ! Socket Role (SERVER,CLIENT,GMSEC_CLIENT)
+fortytwo 4377 ! Server Host Name, Port
+FALSE ! Allow Blocking (i.e. wait on RX)
+FALSE ! Echo to stdout
+1 ! Number of TX prefixes
+"SC[0].Whl[1].H" ! Prefix 0
+********************************** RW 2 to 42 *****************************
+RX ! IPC Mode (OFF,TX,RX,TXRX,ACS,WRITEFILE,READFILE)
+"State01.42" ! File name for WRITE or READ
+SERVER ! Socket Role (SERVER,CLIENT,GMSEC_CLIENT)
+fortytwo 4478 ! Server Host Name, Port
+FALSE ! Allow Blocking (i.e. wait on RX)
+FALSE ! Echo to stdout
+1 ! Number of TX prefixes
+"SC" ! Prefix 0
+********************************** RW 2 from 42 *****************************
+TX ! IPC Mode (OFF,TX,RX,TXRX,ACS,WRITEFILE,READFILE)
+"State02.42" ! File name for WRITE or READ
+SERVER ! Socket Role (SERVER,CLIENT,GMSEC_CLIENT)
+fortytwo 4477 ! Server Host Name, Port
+FALSE ! Allow Blocking (i.e. wait on RX)
+FALSE ! Echo to stdout
+1 ! Number of TX prefixes
+"SC[0].Whl[2].H" ! Prefix 0
+********************************** Torquer IPC *****************************
+RX ! IPC Mode (OFF,TX,RX,TXRX,ACS,WRITEFILE,READFILE)
+"Torquer.Rx" ! File name for WRITE or READ
+SERVER ! Socket Role (SERVER,CLIENT,GMSEC_CLIENT)
+fortytwo 4279 ! Server Host Name, Port
+FALSE ! Allow Blocking (i.e. wait on RX)
+FALSE ! Echo to stdout
+1 ! Number of TX prefixes
+"SC" ! Prefix 0
+********************************** Thruster IPC ****************************
+RX ! IPC Mode (OFF,TX,RX,TXRX,ACS,WRITEFILE,READFILE)
+"Thruster.Rx" ! File name for WRITE or READ
+SERVER ! Socket Role (SERVER,CLIENT,GMSEC_CLIENT)
+fortytwo 4280 ! Server Host Name, Port
+FALSE ! Allow Blocking (i.e. wait on RX)
+FALSE ! Echo to stdout
+1 ! Number of TX prefixes
+"SC" ! Prefix 0
+********************************** GPS IPC *****************************
+TX ! IPC Mode (OFF,TX,RX,TXRX,ACS,WRITEFILE,READFILE)
+"State03.42" ! File name for WRITE or READ
+SERVER ! Socket Role (SERVER,CLIENT,GMSEC_CLIENT)
+fortytwo 4245 ! Server Host Name, Port
+FALSE ! Allow Blocking (i.e. wait on RX)
+FALSE ! Echo to stdout
+1 ! Number of TX prefixes
+"SC[0].GPS[0]" ! Prefix 0
+********************************** CSS IPC *****************************
+TX ! IPC Mode (OFF,TX,RX,TXRX,ACS,WRITEFILE,READFILE)
+"State04.42" ! File name for WRITE or READ
+SERVER ! Socket Role (SERVER,CLIENT,GMSEC_CLIENT)
+fortytwo 4227 ! Server Host Name, Port
+FALSE ! Allow Blocking (i.e. wait on RX)
+FALSE ! Echo to stdout
+1 ! Number of TX prefixes
+"SC[0].CSS" ! Prefix 0
+********************************** MAG IPC *****************************
+TX ! IPC Mode (OFF,TX,RX,TXRX,ACS,WRITEFILE,READFILE)
+"State05.42" ! File name for WRITE or READ
+SERVER ! Socket Role (SERVER,CLIENT,GMSEC_CLIENT)
+fortytwo 4234 ! Server Host Name, Port
+FALSE ! Allow Blocking (i.e. wait on RX)
+FALSE ! Echo to stdout
+1 ! Number of TX prefixes
+"SC[0].MAG" ! Prefix 0
+********************************** Truth data to sim to pass to COSMOS ********************
+TX ! IPC Mode (OFF,TX,RX,TXRX,ACS,WRITEFILE,READFILE)
+"State.42" ! File name for WRITE or READ
+SERVER ! Socket Role (SERVER,CLIENT,GMSEC_CLIENT)
+fortytwo 9999 ! Server Host Name, Port
+FALSE ! Allow Blocking (i.e. wait on RX)
+FALSE ! Echo to stdout
+2 ! Number of TX prefixes
+"SC" ! Prefix 0
+"Orb" ! Prefix 1
+********************************** Write to file for analysis *****************************
+WRITEFILE ! IPC Mode (OFF,TX,RX,TXRX,ACS,WRITEFILE,READFILE)
+"State.42" ! File name for WRITE or READ
+SERVER ! Socket Role (SERVER,CLIENT,GMSEC_CLIENT)
+fortytwo 6008 ! Server Host Name, Port
+FALSE ! Allow Blocking (i.e. wait on RX)
+FALSE ! Echo to stdout
+2 ! Number of TX prefixes
+"SC" ! Prefix 0
+"Orb" ! Prefix 1
+********************************** FSS IPC *****************************
+TX ! IPC Mode (OFF,TX,RX,TXRX,ACS,WRITEFILE,READFILE)
+"FSS.42" ! File name for WRITE or READ
+SERVER ! Socket Role (SERVER,CLIENT,GMSEC_CLIENT)
+fortytwo 4284 ! Server Host Name, Port
+FALSE ! Allow Blocking (i.e. wait on RX)
+FALSE ! Echo to stdout
+1 ! Number of TX prefixes
+"SC[0].FSS[0]" ! Prefix 0
+********************************** IMU IPC *****************************
+TX ! IPC Mode (OFF,TX,RX,TXRX,ACS,WRITEFILE,READFILE)
+"IMU.42" ! File name for WRITE or READ
+SERVER ! Socket Role (SERVER,CLIENT,GMSEC_CLIENT)
+fortytwo 4281 ! Server Host Name, Port
+FALSE ! Allow Blocking (i.e. wait on RX)
+FALSE ! Echo to stdout
+2 ! Number of TX prefixes
+"SC[0].Accel" ! Prefix 0
+"SC[0].Gyro" ! Prefix 1
+********************************** Star Tracker IPC *****************************
+TX ! IPC Mode (OFF,TX,RX,TXRX,ACS,WRITEFILE,READFILE)
+"ST.42" ! File name for WRITE or READ
+SERVER ! Socket Role (SERVER,CLIENT,GMSEC_CLIENT)
+fortytwo 4282 ! Server Host Name, Port
+FALSE ! Allow Blocking (i.e. wait on RX)
+FALSE ! Echo to stdout
+1 ! Number of TX prefixes
+"SC[0].ST" ! Prefix 0
+********************************** EPS IPC *****************************
+TX ! IPC Mode (OFF,TX,RX,TXRX,ACS,WRITEFILE,READFILE)
+"EPS.42" ! File name for WRITE or READ
+SERVER ! Socket Role (SERVER,CLIENT,GMSEC_CLIENT)
+fortytwo 4283 ! Server Host Name, Port
+FALSE ! Allow Blocking (i.e. wait on RX)
+FALSE ! Echo to stdout
+4 ! Number of TX prefixes
+"SC[0].svb" ! Prefix 0
+"SC[0].PosR" ! Prefix 1
+"SC[0].qn" ! Prefix 2
+"Orb[0].PosN" ! Prefix 3
\ No newline at end of file
diff --git a/deployments/nos3/cfg/nos3-mission.xml b/deployments/nos3/cfg/nos3-mission.xml
new file mode 100644
index 000000000..221ecbf93
--- /dev/null
+++ b/deployments/nos3/cfg/nos3-mission.xml
@@ -0,0 +1,23 @@
+
+ 1767225600.0
+
+
+
+
+ yamcs
+
+
+
+ cfs
+
+
+
+ 1
+
+
+
+ sc-full-config.xml
+
+
+
+
diff --git a/deployments/nos3/cfg/sc-fprime-config.xml b/deployments/nos3/cfg/sc-fprime-config.xml
new file mode 100644
index 000000000..b9b6eb4f6
--- /dev/null
+++ b/deployments/nos3/cfg/sc-fprime-config.xml
@@ -0,0 +1,84 @@
+
+
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ true
+
+
+ false
+
+
+ true
+
+
+ true
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+
+ true
+
+
+ -5.0
+ -5.0
+ 5.0
+
+
+ false
+
+
\ No newline at end of file
diff --git a/deployments/nos3/cfg/sc-full-config.xml b/deployments/nos3/cfg/sc-full-config.xml
new file mode 100644
index 000000000..f3fd5ebbd
--- /dev/null
+++ b/deployments/nos3/cfg/sc-full-config.xml
@@ -0,0 +1,84 @@
+
+
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+
+ true
+
+
+ 0.1
+ 1.0
+ -1.0
+
+
+ true
+
+
\ No newline at end of file
diff --git a/deployments/nos3/cfg/sc-minimal-config.xml b/deployments/nos3/cfg/sc-minimal-config.xml
new file mode 100644
index 000000000..e7bfe0919
--- /dev/null
+++ b/deployments/nos3/cfg/sc-minimal-config.xml
@@ -0,0 +1,84 @@
+
+
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ true
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+
+ true
+
+
+ -5.0
+ -5.0
+ 5.0
+
+
+ false
+
+
\ No newline at end of file
diff --git a/deployments/nos3/cfg/sims/nos3-simulator.xml b/deployments/nos3/cfg/sims/nos3-simulator.xml
new file mode 100644
index 000000000..e2f293ae0
--- /dev/null
+++ b/deployments/nos3/cfg/sims/nos3-simulator.xml
@@ -0,0 +1,658 @@
+
+
+
+
+ sim_log_config.xml
+
+
+
+
+
+ 1767225600.0
+ 10000
+ 10000
+ tcp://nos-engine-server:12001
+
+
+
+
+ time
+ true
+ libnos_time_driver.so
+
+ TimeDriver
+
+
+ time
+ tcp://sc01-nos-engine-server:12001
+ command
+ sc1-time-driver
+
+
+ command
+ command
+ time-command
+
+
+
+
+
+
+ stdio-terminal
+ true
+ libsim_terminal.so
+
+ SimTerminal
+
+ STDIO
+ 5555
+ false
+
+
+
+
+
+ command
+ command
+
+ stdio-terminal
+ sample-command
+ ASCII
+ ASCII
+
+
+
+
+
+
+
+ udp-terminal
+ true
+ libsim_terminal.so
+
+ SimTerminal
+
+ UDP
+ 5556
+ false
+
+
+
+
+
+ command
+ command
+
+ udp-terminal
+ sample-command
+ ASCII
+ ASCII
+
+
+
+
+
+
+
+ cmdbus-bridge
+ true
+
+ SIM_CMDBUS_BRIDGE
+
+
+ command
+ command
+ sim-cmdbus-bridge-node
+
+
+
+
+
+
+
+
+
+
+ sample-sim
+ true
+ libsample_sim.so
+
+ SAMPLE
+
+ command
+ command
+ sample-command
+
+ usart
+ usart_16
+ 16
+
+
+
+ SAMPLE_PROVIDER
+
+
+
+
+
+
+ gps
+ true
+ libgps_sim.so
+
+ OEM615
+
+
+ usart
+ usart_1
+ 1
+
+
+
+ GPS42SOCKET
+ fortytwo
+ 4245
+ 30
+ 1
+ 0
+ 0
+ 37
+
+
+
+
+
+
+ camsim
+ true
+ libcam_sim.so
+
+ ARDUCAM_OV5640
+
+ commandcommandcam-command-node
+
+
+
+ 60
+ i2c_2
+
+
+ spi_0
+ 0
+
+
+
+
+
+ generic-eps-sim
+ true
+ libgeneric_eps_sim.so
+
+ GENERIC_EPS
+
+
+ command
+ command
+ eps-command
+
+
+ i2c
+ i2c_1
+ 0x2B
+ 10
+
+
+
+
+ GENERIC_EPS_42_PROVIDER
+ fortytwo
+ 4283
+ 30
+ 1
+ 0
+
+
+
+ 24.0
+ 30.0
+ 1.0
+ 32.0
+ 80.0
+
+
+ sample-command
+ 1.23
+ 4.56
+ 0000
+
+
+ star-tracker-command
+ 3.30
+ 0.25
+ 0000
+
+
+ unknown-sim-command-node
+ 3.30
+ 0.25
+ 0000
+
+
+ unknown-sim-command-node
+ 3.30
+ 0.25
+ 0000
+
+
+ unknown-sim-command-node
+ 3.30
+ 0.25
+ 0000
+
+
+ unknown-sim-command-node
+ 3.30
+ 0.25
+ 0000
+
+
+ unknown-sim-command-node
+ 3.30
+ 0.25
+ 0000
+
+
+ unknown-sim-command-node
+ 12.00
+ 1.23
+ 0000
+
+
+
+
+
+
+ generic-reactionwheel-sim0
+ true
+ libgeneric_rw_sim.so
+
+ GENERICREACTIONWHEELHARDWARE
+
+ commandcommandrw0-command
+ usart
+ usart_2
+ 2
+
+
+
+ GENERICRWSIMDATA42SOCKETPROVIDER
+ fortytwo
+ 4277
+ 4278
+ 30
+ 1
+ 0
+ 0
+
+
+
+
+
+ generic-reactionwheel-sim1
+ true
+ libgeneric_rw_sim.so
+
+ GENERICREACTIONWHEELHARDWARE
+
+ commandcommandrw1-command
+ usart
+ usart_3
+ 3
+
+
+
+ GENERICRWSIMDATA42SOCKETPROVIDER
+ fortytwo
+ 4377
+ 4378
+ 30
+ 1
+ 0
+ 1
+
+
+
+
+
+ generic-reactionwheel-sim2
+ true
+ libgeneric_rw_sim.so
+
+ GENERICREACTIONWHEELHARDWARE
+
+ commandcommandrw2-command
+ usart
+ usart_4
+ 4
+
+
+
+ GENERICRWSIMDATA42SOCKETPROVIDER
+ fortytwo
+ 4477
+ 4478
+ 30
+ 1
+ 0
+ 2
+
+
+
+
+
+ generic-css-sim
+ true
+ libgeneric_css_sim.so
+
+ GENERIC_CSS
+
+
+ command
+ command
+ css-command
+
+
+
+ GENERIC_CSS_42_PROVIDER
+ fortytwo
+ 4227
+ 30
+ 1
+ 0
+ <42-css-scale-factor>1.042-css-scale-factor>
+
+
+ 64
+ i2c_2
+
+
+
+
+
+ generic-torquer-sim
+ true
+ libgeneric_torquer_sim.so
+
+ GENERIC_TORQUER
+
+
+ command
+ command
+ torquer-command
+
+
+
+ GENERIC_TORQUER_42_PROVIDER
+ fortytwo
+ 4279
+ 30
+ 1
+
+
+ 3
+
+
+ 1.42
+ 1.42
+ 1.42
+
+
+
+
+
+ generic-thruster-sim
+ true
+ libgeneric_thruster_sim.so
+
+ GENERIC_THRUSTER
+
+
+ command
+ command
+ thruster-command
+
+ usart
+ usart_29
+ 29
+
+
+
+ GENERIC_THRUSTER_42_PROVIDER
+ fortytwo
+ 4280
+ 30
+ 1
+
+
+
+
+
+ truth42sim
+ true
+ libtruth_42_sim.so
+
+ TRUTH42
+ cosmos
+ 5111
+ 1.0
+ 100
+
+
+ commandcommandtruth42-sim-command-node
+
+
+ TRUTH42PROVIDER
+ fortytwo
+ 9999
+ 30
+ 1
+ 0
+ 0
+
+
+
+
+
+ generic-fss-sim
+ true
+ libgeneric_fss_sim.so
+
+ GENERIC_FSS
+
+ commandcommandfss-command
+ spi
+ spi_1
+ 1
+
+
+
+ GENERIC_FSS_42_PROVIDER
+ fortytwo
+ 4284
+ 30
+ 1
+ 0
+
+
+
+
+
+ generic-radio-sim
+ true
+ libgeneric_radio_sim.so
+
+ GENERIC_RADIO
+
+
+ command
+ command
+ radio-sim-command-node
+
+
+ fsw
+ nos-fsw
+ 5010
+ 5011
+ 5015
+
+
+ radio
+ radio-sim
+ 5014
+
+
+ gsw
+
+
+
+
+
+ cryptolib
+ 8010
+ 8011
+
+
+ prox
+ radio-sim
+ 7012
+ 7010
+ 7011
+ 7012
+
+
+
+ GENERIC_RADIO_PROVIDER
+
+
+
+
+
+ generic-imu-sim
+ true
+ libgeneric_imu_sim.so
+
+ GENERIC_IMU
+
+ command
+ command
+ imu-command
+
+ can
+ can_0
+ 15
+
+
+
+ GENERIC_IMU_42_PROVIDER
+ fortytwo
+ 4281
+ 30
+ 1
+ 0
+
+
+
+
+
+ generic-mag-sim
+ true
+ libgeneric_mag_sim.so
+
+ GENERIC_MAG
+
+ command
+ command
+ mag-command
+
+ spi
+ spi_2
+ 2
+
+
+
+ GENERIC_MAG_42_PROVIDER
+ fortytwo
+ 4234
+ 30
+ 1
+ 0
+
+
+
+
+
+ generic-star-tracker-sim
+ true
+ libgeneric_star_tracker_sim.so
+
+ GENERIC_STAR_TRACKER
+
+ command
+ command
+ star-tracker-command
+
+ usart
+ usart_10
+ 10
+
+
+
+ GENERIC_STAR_TRACKER_42_PROVIDER
+ fortytwo
+ 4282
+ 30
+ 1
+ 0
+ 0
+
+
+
+
+
+
diff --git a/deployments/nos3/cfg/sims/nos_engine_server_config.json b/deployments/nos3/cfg/sims/nos_engine_server_config.json
new file mode 100644
index 000000000..aea5ced90
--- /dev/null
+++ b/deployments/nos3/cfg/sims/nos_engine_server_config.json
@@ -0,0 +1,17 @@
+{
+ "plugins": [
+ "uart"
+ ],
+
+ "server_uris": [
+ {
+ "name": "fsw",
+ "server_uri": "tcp://nos-engine-server:12000"
+ },
+ {
+ "name": "nos3",
+ "server_uri": "tcp://nos-engine-server:12001"
+ }
+ ]
+}
+
diff --git a/deployments/nos3/cfg/sims/sim_log_config.xml b/deployments/nos3/cfg/sims/sim_log_config.xml
new file mode 100644
index 000000000..d1e5ea3d7
--- /dev/null
+++ b/deployments/nos3/cfg/sims/sim_log_config.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/deployments/scripts/env.sh b/deployments/scripts/env.sh
new file mode 100755
index 000000000..57feeb579
--- /dev/null
+++ b/deployments/scripts/env.sh
@@ -0,0 +1,175 @@
+#!/usr/bin/env bash
+
+set -e
+
+# A script to generate .env file for container deployments
+
+# Simulation specific inputs
+NOW=$(date -u +"%Y-%m-%d %H:%M:%S UTC")
+SIM_T0_DATETIME="2026-01-01 00:00:00 UTC"
+SIM_T0_DATETIME="${NOW}"
+
+TIME_INTERVAL=3600 # 1 hour in seconds
+TIME_INTERVAL_MILLISECONDS=$( expr ${TIME_INTERVAL} \* 1000 )
+
+#SIM_T0_DATETIME="${NOW}"
+LEAP_SECONDS=37
+
+GSW_SOFTWARE=yamcs
+FSW_SOFTWARE=cfs
+
+# This is to define either the standard path, i.e. ${PWD}/nos3, or a local definition
+NOS3_CFG_PATH=..
+NOS3_MISSION_CFG_FILE=../cfg/nos3-mission.xml
+
+NOS3_GIT_URL=https://github.com/nasa/nos3
+NOS3_GIT_COMMIT=dev
+
+NOS3_IMAGE_URI=docker.io/ivvitc/nos3-64:20251107
+NOS3_BASE_DIR=/home/nos3/.nos3
+
+PROJECT=${PROJECT:-nos3}
+MISSION=${MISSION:-m01}
+SPACECRAFT=${SPACECRAFT:-sc01}
+PROJECT_NAME=${PROJECT}-${MISSION}-${SPACECRAFT}
+PROJECT_MISSION=${PROJECT}-${MISSION}
+SC_ENVIRO=${SC_ENVIRO:-sim}
+
+FORTYTWO_HOST=${FORTYTWO_HOST:-localhost}
+FORTYTWO_PORT=${FORTYTWO_PORT:-80}
+FORTYTWO_HOST_PORT=${FORTYTWO_HOST_PORT:-30090}
+
+YAMCS_HOST=${YAMCS_HOST:-localhost}
+YAMCS_PORT=${YAMCS_PORT:-8090}
+YAMCS_HOST_PORT=${YAMCS_HOST_PORT:-8090}
+
+OPENMCT_HOST=${OPENMCT_HOST:-localhost}
+OPENMCT_PORT=${OPENMCT_PORT:-9000}
+OPENMCT_HOST_PORT=${OPENMCT_HOST_PORT:-9000}
+
+########################################################################
+# Avoid updating the below variables unless you know what you are doing
+########################################################################
+
+# Time specific derivations
+TIME_OFFSET_SECONDS=+30 # to simulate 30 seconds into the future from SIM_T0_DATETIME
+J2000_REFERENCE_DATETIME="2000-01-01 12:00:00 UTC"
+SIM_T0_EPOCH_SECONDS=$(date -ud "${SIM_T0_DATETIME} ${TIME_OFFSET_SECONDS} seconds" +"%s")
+SIM_T0_EPOCH_MILLISECONDS=$( expr ${SIM_T0_EPOCH_SECONDS} \* 1000 )
+SIM_TF_EPOCH_MILLISECONDS=$( expr ${SIM_T0_EPOCH_MILLISECONDS} + ${TIME_INTERVAL} \* 1000 )
+J2000_EPOCH_SECONDS=$(date -ud "${J2000_REFERENCE_DATETIME}" +"%s")
+
+cat << EOF
+#
+# Auto-generated by ${PWD}/$(basename $0) on $(date -u +"%Y-%m-%dT%H:%M:%S%z")
+#
+
+PROJECT=${PROJECT}
+FLEET=${PROJECT}
+MISSION=${MISSION}
+SPACECRAFT=${SPACECRAFT}
+PROJECT_NAME=${PROJECT_NAME}
+PROJECT_MISSION=${PROJECT_MISSION}
+COMPOSE_PROJECT_NAME=${PROJECT_NAME}
+SC_ENVIRO=${SC_ENVIRO}
+
+# nos3 specific configurations
+NOS3_GIT_URL=${NOS3_GIT_URL}
+NOS3_GIT_COMMIT=${NOS3_GIT_COMMIT}
+NOS3_DIR=/home/nos3/.nos3
+NOS3_USER=nos3
+NOS3_IMAGE_URI=${NOS3_IMAGE_URI}
+NOS3_BASE_DIR=${NOS3_BASE_DIR}
+
+# This is to define either the standard path, i.e. ${PWD}/nos3, or a local definition
+NOS3_CFG_PATH=${NOS3_CFG_PATH}
+NOS3_MISSION_CFG_FILE=${NOS3_MISSION_CFG_FILE}
+
+# Simulation specific configurations
+SIM_T0_DATETIME="${SIM_T0_DATETIME}"
+J2000_REFERENCE_DATETIME="${J2000_REFERENCE_DATETIME}"
+
+TIME_OFFSET_SECONDS=${TIME_OFFSET_SECONDS}
+TIME_INTERVAL=${TIME_INTERVAL}
+TIME_INTERVAL_MILLISECONDS=${TIME_INTERVAL_MILLISECONDS}
+
+# In epoch seconds
+SIM_T0_EPOCH_SECONDS=${SIM_T0_EPOCH_SECONDS}
+SIM_T0_EPOCH_MILLISECONDS=${SIM_T0_EPOCH_MILLISECONDS}
+SIM_TF_EPOCH_MILLISECONDS=${SIM_TF_EPOCH_MILLISECONDS}
+J2000_EPOCH_SECONDS=${J2000_EPOCH_SECONDS}
+
+# In J2000 seconds
+START_TIME=$((${SIM_T0_EPOCH_SECONDS}-${J2000_EPOCH_SECONDS}))
+
+# Deployment specific configurations
+DEPLOYMENT_ENVIRO=${DEPLOYMENT_ENVIRO}
+HEALTHCHECK_PORT=${HEALTHCHECK_PORT}
+
+# Proxy configurations, the proxy values come for the environment,
+# which is important for the vmmoc
+HTTPS_PROXY=${HTTPS_PROXY}
+HTTP_PROXY=${HTTP_PROXY}
+NO_PROXY=${NO_PROXY}
+
+# Maven/Java specific configurations
+MAVEN_REPO_LOCAL=/home/nos3/.nos3/.m2
+MAVEN_HTTPS_PROXY="--settings ./settings.xml"
+MAVEN_SETTINGS_FILE=${MAVEN_SETTINGS_FILE}
+
+# 42 specific configurations
+FORTYTWO_DISPLAY=:1
+FORTYTWO_GIT_URL=https://github.com/nasa-itc/42.git
+FORTYTWO_GIT_COMMIT=nos3-main
+FORTYTWO_GIT_FOLDER=42
+FORTYTWO_STARTUP_FOLDER=NOS3InOut
+FORTYTWO_RECOMPILE=false
+FORTYTWO_BASE_DIR=/opt/nasa-itc
+FORTYTWO_VNC_PASSWORD=$(echo -n "foobar" | shasum -a 256 | cut -d" " -f1)
+
+FORTYTWO_HOST=${FORTYTWO_HOST}
+FORTYTWO_PORT=${FORTYTWO_PORT}
+FORTYTWO_HOST_PORT=${FORTYTWO_HOST_PORT}
+
+FORTYTWO_SIM_DATE="$(date -ud "@${SIM_T0_EPOCH_SECONDS}" +"%m %d %Y")"
+FORTYTWO_SIM_TIME="$(date -ud "@${SIM_T0_EPOCH_SECONDS}" +"%H %M %S.%2N")"
+FORTYTWO_LEAP_SECONDS=${LEAP_SECONDS}
+
+# gsw+fsw specific configurations
+GSW_SOFTWARE=${GSW_SOFTWARE}
+FSW_SOFTWARE=${FSW_SOFTWARE}
+
+COMPONENT_DIR=/home/nos3/builds/nos3/components
+
+# yamcs specific configurations
+YAMCS_DATA_DIR=/home/nos3/.nos3/yamcs/target/yamcs/yamcs-data
+# YAMCS_ETC_DIR=/home/nos3/.nos3/yamcs/target/yamcs/yamcs-etc
+
+# YAMCS_CACHE_DIR=/home/nos3/.nos3/yamcs/target/yamcs/yam
+
+YAMCS_GIT_URL=https://github.com/nasa-itc/nos3_yamcs_master.git
+YAMCS_GIT_COMMIT=nos3-dev
+
+YAMCS_INSTANCES=nos3
+
+YAMCS_HOST=${YAMCS_HOST}
+YAMCS_HOST_PORT=${YAMCS_HOST_PORT}
+YAMCS_PORT=${YAMCS_PORT}
+
+# openmct specific configurations
+OPENMCT_IMAGE_URI=docker.io/library/ubuntu:25.04
+
+OPENMCT_GIT_URL=https://github.com/akhenry/openmct-yamcs.git
+OPENMCT_GIT_COMMIT=master
+
+OPENMCT_NVM_VERSION=v0.40.1
+OPENMCT_NODE_VERSION=v21.7.3
+
+OPENMCT_HOST=${OPENMCT_HOST}
+OPENMCT_PORT=${OPENMCT_PORT}
+OPENMCT_HOST_PORT=${OPENMCT_HOST_PORT}
+
+# nasa-itc specific configurations
+BASE_DIR=/opt/nasa-itc
+
+EOF
\ No newline at end of file
diff --git a/deployments/scripts/generate-k8s.sh b/deployments/scripts/generate-k8s.sh
new file mode 100755
index 000000000..bdd0a9b13
--- /dev/null
+++ b/deployments/scripts/generate-k8s.sh
@@ -0,0 +1,551 @@
+#!/bin/bash -e
+
+RECREATE=${1:-no} # recreate namespaces, pods, etc.
+PROVIDER=${2:-podman} #
+
+MY_PWD=${PWD}
+
+#source ./scripts/functions/deployment.sh
+
+#------------------------------------------------------------------------------
+# A script to generate k8s deployment, service... yaml files and apply them
+#------------------------------------------------------------------------------
+
+#------------------------------------------------------------------------------
+# TODO: figure out podman and arm64 for nos3
+export KIND_EXPERIMENTAL_PROVIDER=${PROVIDER}
+
+K8S_CONTEXT=docker-desktop
+NODE_SELECTOR=docker-desktop
+
+K8S_MODE='docker-desktop' # if this value is different from K8S_CONTEXT, target remote k8s on aws
+
+# kubectl --context kind-myorg-dev-missions-exp apply -k .
+#------------------------------------------------------------------------------
+NOS3_CONFIG=$(cat ./scripts/nos3.yaml | yq 'explode(.)' | grep -iv '^null$')
+#------------------------------------------------------------------------------
+
+#------------------------------------------------------------------------------
+# Mission/SC/Component Arrays
+#------------------------------------------------------------------------------
+OU=$(echo "${NOS3_CONFIG}" | yq '.metadata.OU')
+ENVIRO=$(echo "${NOS3_CONFIG}" | yq '.metadata.ENVIRO')
+TENANT=$(echo "${NOS3_CONFIG}" | yq '.metadata.TENANT')
+PURPOSE=$(echo "${NOS3_CONFIG}" | yq '.metadata.PURPOSE')
+CONTEXT=$(echo "${NOS3_CONFIG}" | yq '.metadata.CONTEXT')${PURPOSE} # -exp must exist to match remote cluster system
+
+REALM=${OU}-${ENVIRO}-${CONTEXT}
+
+nodeSelector=$(echo "${NOS3_CONFIG}" | yq '.defaults.spec.nodes.nodeSelector') # will be overridden if target k8s is remote
+imagePullPolicy=$(echo "${NOS3_CONFIG}" | yq '.defaults.spec.nodes.imagePullPolicy')
+HEALTHCHECK_PORT=$(echo "${NOS3_CONFIG}" | yq '.defaults.spec.HEALTHCHECK_PORT')
+
+MISSIONS=($(echo "$NOS3_CONFIG" | yq '.missions[] | keys[]'))
+#------------------------------------------------------------------------------
+
+#------------------------------------------------------------------------------
+# Creds
+#------------------------------------------------------------------------------
+MISSION_GIT_USER=$(cat ~/.appdat/personal_access_token)
+MISSION_GIT_TOKEN=$(cat ~/.appdat/personal_access_token)
+MISSION_GIT_SERVER=appdat.jsc.nasa.gov
+
+K8S_LOCAL=true # to know if k8s is local
+#------------------------------------------------------------------------------
+
+#------------------------------------------------------------------------------
+# K8s local or remote
+#------------------------------------------------------------------------------
+if [ "${K8S_MODE}" != "${K8S_CONTEXT}" ]; then
+
+ AWS_CLUSTER_NAME=${OU}-${ENVIRO}-${CONTEXT}
+
+ ENVIRO=$(echo ${AWS_CLUSTER_NAME} | cut -d- -f2)
+
+ # All this is to capitalize the first letter!
+ ENVIRO_USE=`echo ${ENVIRO:0:1} | tr '[a-z]' '[A-Z]'`${ENVIRO:1}
+
+ echo; echo "Cluster name: $AWS_CLUSTER_NAME"
+
+ mkdir -p /tmp/${HOME}/development/APPDAT/appdat.jsc.nasa.gov/ssmo/SSMO-APPDAT/
+ cd /tmp/${HOME}/development/APPDAT/appdat.jsc.nasa.gov/ssmo/SSMO-APPDAT/
+
+ git clone git@appdat.jsc.nasa.gov:ssmo/SSMO-APPDAT/sysconf.git || true
+ cd ./sysconf/admin
+
+ make kubeconfig AWS_CLUSTER_NAME=${AWS_CLUSTER_NAME}
+ source /tmp/aws-credentials_SSMO-Appdat-Hybrid-Gov${ENVIRO_USE}.sh
+
+ cd ${MY_PWD} # critical
+
+ K8S_CONTEXT=arn:aws-us-gov:eks:${AWS_REGION}:${AWS_ACCOUNT}:cluster/${AWS_CLUSTER_NAME}
+
+ imagePullPolicy=Always
+ K8S_LOCAL=false
+fi
+
+echo; echo Using and setting context ${K8S_CONTEXT}...
+kubectl config use-context ${K8S_CONTEXT}
+kubectl config set-context ${K8S_CONTEXT}
+#------------------------------------------------------------------------------
+
+#------------------------------------------------------------------------------
+# Image URI with its creds
+#------------------------------------------------------------------------------
+# TODO: put these in one file
+IMAGE_REGISTRY=registry.appdat.jsc.nasa.gov
+#IMAGE_URI=${IMAGE_REGISTRY}/ssmo/images/ssmo/nos3/nos3-base:20250217
+
+IMAGE_REGISTRY_USER=${USER}
+IMAGE_REGISTRY_PASSWORD=$(cat ~/.appdat/images/SSMO_IMAGES)
+
+IMAGE_SECRET_NAME=appdat-registry
+
+cat <<-EOF > /tmp/config.json
+{
+ "auths": {
+ "${IMAGE_REGISTRY}": {
+ "auth": "$(echo -n ${IMAGE_REGISTRY_USER}:${IMAGE_REGISTRY_PASSWORD} | base64 | tr -d '\n')",
+ "username": "${IMAGE_REGISTRY_USER}",
+ "password": "${IMAGE_REGISTRY_PASSWORD}"
+ }
+ }
+}
+EOF
+
+#------------------------------------------------------------------------------
+# Missions Loop
+#------------------------------------------------------------------------------
+for MISSION in "${MISSIONS[@]}"
+do
+
+ _MISSION_ENABLED=$(echo "${NOS3_CONFIG}" | yq ".missions[].${MISSION}.enabled" | grep -iv '^null$')
+
+ if [ "${_MISSION_ENABLED}" != "true" ]; then
+ echo; echo MISSION ${MISSION} is not enabled, add enabled: true, skipping...
+ continue
+ else
+ echo; echo MISSION ${MISSION} is enabled continuing.
+ fi
+
+ MISSION_PATH=./environments/${OU}/${ENVIRO}/${CONTEXT}/${MISSION}
+ mkdir -p ${MISSION_PATH}
+
+ KUSTOMIZATION_FILE_MISSION=${MISSION_PATH}/kustomization.yaml
+ touch ${KUSTOMIZATION_FILE_MISSION}
+
+ # Printout preamble of kustomization file
+ cat <<-EOF > ${KUSTOMIZATION_FILE_MISSION}
+---
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+resources:
+EOF
+
+ SPACECRAFT=($(echo "${NOS3_CONFIG}" | yq ".missions[].${MISSION}.spacecraft[] | keys[]"))
+
+ #------------------------------------------------------------------------------
+ # Generate deployment and service yaml files
+ # run is below that
+ #------------------------------------------------------------------------------
+ for SC in "${SPACECRAFT[@]}"
+ do
+
+ _SPACECRAFT_ENABLED=$(echo "${NOS3_CONFIG}" | yq ".missions[].${MISSION}.spacecraft[].${SC}.enabled" | grep -iv '^null$')
+
+ if [ "${_SPACECRAFT_ENABLED}" != "true" ]; then
+ echo " MISSION ${MISSION} is enabled, but SPACECRAFT ${SC} is not enabled, add enabled: true, skipping..."
+ continue
+ else
+ echo " MISSION ${MISSION} is enabled, and SPACECRAFT ${SC} is enabled continuing."
+ fi
+
+ if [ "${K8S_LOCAL}" != "true" ]; then
+ nodeSelector=$(
+ cat <> ${KUSTOMIZATION_FILE_MISSION}
+ - ./${SC}/kubernetes
+EOF
+echo " Created ${KUSTOMIZATION_FILE_MISSION}"
+
+ SPACECRAFT_PATH=${MISSION_PATH}/${SC}
+ mkdir -p ${SPACECRAFT_PATH}
+
+ SPACECRAFT_PATH_K8S=${SPACECRAFT_PATH}/kubernetes
+ mkdir -p ${SPACECRAFT_PATH_K8S}
+
+ KUSTOMIZATION_FILE_SC=${SPACECRAFT_PATH_K8S}/kustomization.yaml
+ rm -f ${KUSTOMIZATION_FILE_SC}
+ touch ${KUSTOMIZATION_FILE_SC}
+
+ SPACECRAFT_PATH_DOCKER=${SPACECRAFT_PATH}/docker/${K8S_NAMESPACE}
+ mkdir -p ${SPACECRAFT_PATH_DOCKER}
+
+ #------------------------------------------------------------------------------
+ # docker-compose
+ #------------------------------------------------------------------------------
+ DOCKER_COMPOSE_FILE=${SPACECRAFT_PATH_DOCKER}/${K8S_NAMESPACE}_docker-compose.yaml
+
+ rm -rf ${DOCKER_COMPOSE_FILE}
+ touch ${DOCKER_COMPOSE_FILE}
+
+ cat <<-EOF > ${DOCKER_COMPOSE_FILE}
+# generated on $(date -u "+%YT%T")
+
+services:
+EOF
+echo " Created ${DOCKER_COMPOSE_FILE}"
+ #------------------------------------------------------------------------------
+
+ # create configMap per namespace TODO: how to use
+CONFIGMAP_FILE=${SPACECRAFT_PATH_K8S}/${K8S_NAMESPACE}_configmap.yaml
+
+ cat <<-EOF > ${CONFIGMAP_FILE}
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: ${K8S_NAMESPACE}-configmap
+ namespace: ${K8S_NAMESPACE}
+data:
+ key1: value1
+EOF
+echo " Created ${CONFIGMAP_FILE}.yaml"
+
+ # Print preamble of kustomization file
+ cat <<-EOF > ${KUSTOMIZATION_FILE_SC}
+---
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+resources:
+EOF
+echo " Created blank ${KUSTOMIZATION_FILE_SC}"
+
+ #------------------------------------------------------------------------------
+ # By component
+ #------------------------------------------------------------------------------
+ COMPONENTS=($(echo "${NOS3_CONFIG}" | yq ".missions[].${MISSION}.spacecraft[].${SC}.components" | grep -iv '^null$' | yq ' keys[]'))
+
+ THINGS=(${COMPONENTS[@]})
+
+ _COMPONENTS=$(echo "${NOS3_CONFIG}" | yq ".missions[].${MISSION}.spacecraft[].${SC}.components" | grep -iv '^null$')
+
+ for COMPONENT in "${THINGS[@]}"
+ do
+
+ COMPONENTS=($(echo "${_COMPONENTS}" | yq ' keys[]'))
+
+ _CONTAINER=$(echo "${_COMPONENTS}" | yq ".${COMPONENT}.container" | grep -iv '^null$')
+
+ # TODO: get replicas: value
+# REPLICAS=$(echo "${_COMPONENTS}" | yq ".${COMPONENT}.container.replicas" | grep -iv '^null$')
+
+ REPLICAS=1
+ CONTAINER_RESOURCES=$(echo "${_CONTAINER}" | yq ".resources" | grep -iv '^null$')
+
+ CONTAINER_LIMITS_MEMORY=$(echo "${CONTAINER_RESOURCES}"| yq ".limits.memory" | grep -iv '^null$' )
+ CONTAINER_LIMITS_CPU=$(echo "${CONTAINER_RESOURCES}"| yq ".limits.cpu" | grep -iv '^null$' )
+
+ CONTAINER_REQUESTS_MEMORY=$(echo "${CONTAINER_RESOURCES}"| yq ".requests.memory" | grep -iv '^null$' )
+ CONTAINER_REQUESTS_CPU=$(echo "${CONTAINER_RESOURCES}"| yq ".requests.cpu" | grep -iv '^null$' )
+
+ _PATHS=$(echo "${_CONTAINER}" | yq ".PATHS" | grep -iv '^null$')
+ CONTAINER_PATHS=($(echo "${_PATHS}"))
+
+ BASE_DIR=$(echo "${_PATHS}" | yq '.BASE_DIR')
+ SIM_BIN=$(echo "${_PATHS}" | yq '.SIM_BIN')
+ SIMULATOR_BIN=$(echo "${_PATHS}" | yq '.SIMULATOR_BIN')
+ LOG_CONFIG=$(echo "${_PATHS}" | yq '.LOG_CONFIG')
+
+# TODO: note SC_CFG_FILE below
+ CFG_FILE=$(echo "${_PATHS}" | yq '.SC_CFG_FILE')
+# TODO: TBD wrt to GND
+ GND_CFG_FILE=$(echo "${_PATHS}" | yq '.GND_CFG_FILE')
+
+ IMAGE_URI=$(echo "${_PATHS}" | yq '.IMAGE_URI')
+
+ METADATA_NAME=${MISSION}-${SC}-${COMPONENT}
+ DEPLOYMENT=${MISSION}-${SC}-${COMPONENT}
+
+ COMPONENT_PATH=${SPACECRAFT_PATH_K8S}/${COMPONENT}
+ mkdir -p ${COMPONENT_PATH}
+
+ KUSTOMIZATION_FILE_COMPONENT=${COMPONENT_PATH}/kustomization.yaml
+ rm -f ${KUSTOMIZATION_FILE_COMPONENT}
+
+ #------------------------------------------------------------------------------
+ # Generate Kustomization files
+ #------------------------------------------------------------------------------
+ cat <<-EOF >> ${KUSTOMIZATION_FILE_COMPONENT}
+---
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+resources:
+ - ./deployment.yaml
+ - ./service.yaml
+EOF
+echo " Created ${KUSTOMIZATION_FILE_COMPONENT}"
+
+ SERVICE_FILE_COMPONENT=${COMPONENT_PATH}/service.yaml
+ rm -f ${SERVICE_FILE_COMPONENT}
+
+ cat <<-EOF >> ${KUSTOMIZATION_FILE_SC}
+ - ./${COMPONENT}
+EOF
+echo " Added component ${COMPONENT} to ${SERVICE_FILE_COMPONENT}"
+
+ DEPLOYMENT_FILE_COMPONENT=${COMPONENT_PATH}/deployment.yaml
+ rm -f ${DEPLOYMENT_FILE_COMPONENT}
+
+ #------------------------------------------------------------------------------
+ # Generate Deployment files
+ #------------------------------------------------------------------------------
+ cat <<-EOF >> ${DEPLOYMENT_FILE_COMPONENT}
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ namespace: ${K8S_NAMESPACE}
+ name: ${K8S_NAMESPACE}-${COMPONENT}
+ labels:
+ app: ${K8S_NAMESPACE}-${COMPONENT}
+spec:
+ replicas: 0
+ selector:
+ matchLabels:
+ app: ${K8S_NAMESPACE}-${COMPONENT}
+ template:
+ metadata:
+ labels:
+ app: ${K8S_NAMESPACE}-${COMPONENT}
+ spec:
+ imagePullSecrets:
+ - name: appdat-registry
+${nodeSelector}
+ containers:
+ - name: ${K8S_NAMESPACE}-${COMPONENT}
+ image: ${IMAGE_URI}
+ imagePullPolicy: ${imagePullPolicy}
+ resources:
+ limits:
+ memory: ${CONTAINER_LIMITS_MEMORY}
+ cpu: ${CONTAINER_LIMITS_CPU}
+ requests:
+ memory: ${CONTAINER_REQUESTS_MEMORY}
+ cpu: ${CONTAINER_REQUESTS_CPU}
+ ports:
+ - containerPort: ${HEALTHCHECK_PORT}
+ env:
+ - name: HEALTHCHECK_PORT
+ value: "${HEALTHCHECK_PORT}"
+ - name: OU
+ value: ${OU}
+ - name: TENANT
+ value: ${TENANT}
+ - name: ENVIRO
+ value: ${ENVIRO}
+ - name: CONTEXT
+ value: missions
+ - name: MISSION
+ value: ${MISSION}
+ - name: SPACECRAFT
+ value: ${SC}
+ - name: MISSION_GIT_USER
+ value: ${MISSION_GIT_USER}
+ - name: MISSION_GIT_TOKEN
+ value: ${MISSION_GIT_TOKEN}
+ - name: MISSION_GIT_SERVER
+ value: ${MISSION_GIT_SERVER}
+ - name: BASE_DIR
+ value: ${BASE_DIR}
+ - name: SIM_BIN
+ value: ${SIM_BIN}
+ - name: SIMULATOR_BIN
+ value: ${SIMULATOR_BIN}
+ - name: LOG_CONFIG
+ value: ${LOG_CONFIG}
+ - name: CFG_FILE
+ value: ${CFG_FILE}
+ # - name: SC_CFG_FILE
+ # value: ${SC_CFG_FILE}
+ # - name: GND_CFG_FILE
+ # value: ${GND_CFG_FILE}
+ - name: NETWORK
+ value: ${K8S_NAMESPACE}
+ - name: COMPONENT_NAME
+ value: ${COMPONENT}
+EOF
+echo " Created ${DEPLOYMENT_FILE_COMPONENT}"
+
+ #------------------------------------------------------------------------------
+ # Generate Service files
+ #------------------------------------------------------------------------------
+ cat <<-EOF >> ${SERVICE_FILE_COMPONENT}
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: ${COMPONENT}
+ namespace: ${K8S_NAMESPACE}
+spec:
+ selector:
+ app: ${K8S_NAMESPACE}-${COMPONENT}
+ ports:
+ - name: healthcheck-tcp
+ protocol: TCP
+ port: ${HEALTHCHECK_PORT}
+ targetPort: ${HEALTHCHECK_PORT}
+ - name: healthcheck-udp
+ protocol: UDP
+ port: ${HEALTHCHECK_PORT}
+ targetPort: ${HEALTHCHECK_PORT}
+ type: ClusterIP
+
+EOF
+echo " Created ${SERVICE_FILE_COMPONENT}"
+
+ #------------------------------------------------------------------------------
+ # docker-compose: services
+ #------------------------------------------------------------------------------
+ SERVICE_NAME=${K8S_NAMESPACE}-${COMPONENT}
+
+ cat <<-EOF >> ${DOCKER_COMPOSE_FILE}
+ ${SERVICE_NAME}:
+ image: ${IMAGE_URI}
+ container_name: ${SERVICE_NAME}
+ hostname: ${SERVICE_NAME}
+ environment:
+ HEALTHCHECK_PORT: ${HEALTHCHECK_PORT}
+ OU: ${OU}
+ TENANT: ${TENANT}
+ ENVIRO: ${ENVIRO}
+ CONTEXT: missions
+ MISSION: ${MISSION}
+ SPACECRAFT: ${SC}
+ MISSION_GIT_USER: ${MISSION_GIT_USER}
+ MISSION_GIT_TOKEN: ${MISSION_GIT_TOKEN}
+ MISSION_GIT_SERVER: ${MISSION_GIT_SERVER}
+ BASE_DIR: ${BASE_DIR}
+ SIM_BIN: ${SIM_BIN}
+ NOS3_SIMULATOR_BIN: ${SIMULATOR_BIN}
+ LOG_CONFIG: ${LOG_CONFIG}
+ CFG_FILE: ${CFG_FILE}
+ # SC_CFG_FILE: ${SC_CFG_FILE}
+ # GND_CFG_FILE: ${GND_CFG_FILE}
+ COMPONENT_NAME: ${COMPONENT}
+ privileged: true
+ networks:
+ - ${K8S_NAMESPACE}
+EOF
+
+ echo $MISSION $SC $COMPONENT ${SPACECRAFT[@]}
+
+ done # COMPONENT loop
+
+ #------------------------------------------------------------------------------
+ # docker-compose completion for sc
+ #------------------------------------------------------------------------------
+ cat <<-EOF >> ${DOCKER_COMPOSE_FILE}
+
+networks:
+ ${K8S_NAMESPACE}:
+ driver: bridge
+
+EOF
+ #------------------------------------------------------------------------------
+
+ done # SC loop
+
+done # MISSIONS loop
+
+#------------------------------------------------------------------------------
+# Run kubectl on created yaml files
+#------------------------------------------------------------------------------
+# Missions Loop
+for MISSION in "${MISSIONS[@]}"
+do
+
+ MISSION_PATH=./environments/${OU}/${ENVIRO}/${CONTEXT}/${MISSION}
+
+ _MISSION_ENABLED=$(echo "${NOS3_CONFIG}" | yq ".missions[].${MISSION}.enabled" | grep -iv '^null$')
+
+ if [ "${_MISSION_ENABLED}" != "true" ]; then
+ echo; echo MISSION ${MISSION} is not enabled, add enabled: true, skipping...
+ continue
+ else
+ echo; echo MISSION ${MISSION} is enabled continuing.
+ fi
+
+ # resource names, to be used with context, cluster, namespace, pods, and services
+ RESOURCE_NAME=${REALM}-${MISSION}
+
+ REALM=docker-desktop
+
+ # contexts/clusters
+ K8S_CONTEXT=${REALM}
+ K8S_CLUSTER=${K8S_CONTEXT}
+ K8S_USER=${K8S_CONTEXT}
+
+# kind delete cluster --name ${K8S_CLUSTER} || true
+# kind create cluster --name ${K8S_CLUSTER} || true
+
+# K8S_CONTEXT_PREFIX=kind-
+ K8S_CONTEXT=${K8S_CONTEXT_PREFIX}${K8S_CONTEXT}
+
+ # set and use context
+ kubectl config set-context ${K8S_CONTEXT}
+ kubectl config use-context ${K8S_CONTEXT}
+ SPACECRAFT=($(echo "${NOS3_CONFIG}" | yq ".missions[].${MISSION}.spacecraft[] | keys[]"))
+
+ for SC in "${SPACECRAFT[@]}"
+ do
+
+ _SPACECRAFT_ENABLED=$(echo "${NOS3_CONFIG}" | yq ".missions[].${MISSION}.spacecraft[].${SC}.enabled" | grep -iv '^null$')
+
+ if [ "${_SPACECRAFT_ENABLED}" != "true" ]; then
+ echo " MISSION ${MISSION} is enabled, but SPACECRAFT ${SC} is not enabled, add enabled: true, skipping..."
+ continue
+ else
+ echo " MISSION ${MISSION} is enabled, and SPACECRAFT ${SC} is enabled continuing."
+ fi
+
+ K8S_NAMESPACE=${OU}-${ENVIRO}-${CONTEXT}-${MISSION}-${SC}
+
+ if [ "${RECREATE}" == "yes" ]; then
+ echo; echo Deleting namespace ${K8S_NAMESPACE} in context ${K8S_CONTEXT}...
+ kubectl delete namespace ${K8S_NAMESPACE} --context ${K8S_CONTEXT} || true
+ fi
+
+ echo; echo Creating namespace ${K8S_NAMESPACE} in context ${K8S_CONTEXT}...
+ kubectl create namespace ${K8S_NAMESPACE} --context ${K8S_CONTEXT} || true
+
+ echo; echo Creating secret ${IMAGE_SECRET_NAME} in namespace ${K8S_NAMESPACE} in context ${K8S_CONTEXT}...
+ kubectl delete secret ${IMAGE_SECRET_NAME} -n ${K8S_NAMESPACE} || true
+ kubectl create secret generic ${IMAGE_SECRET_NAME} \
+ --from-file=.dockerconfigjson=/tmp/config.json \
+ --type=kubernetes.io/dockerconfigjson \
+ -n ${K8S_NAMESPACE}
+ kubectl get secret ${IMAGE_SECRET_NAME} --output=yaml -n ${K8S_NAMESPACE}
+
+ SPACECRAFT_PATH=${MISSION_PATH}/${SC}
+ SPACECRAFT_PATH_K8S=${SPACECRAFT_PATH}/kubernetes
+
+ KUSTOMIZATION_FILE_SC=${SPACECRAFT_PATH_K8S}
+
+ echo; echo Applying ${KUSTOMIZATION_FILE_SC} in context ${K8S_CONTEXT}...
+ kubectl apply -k ${KUSTOMIZATION_FILE_SC}
+ kubectl get deployments -n ${K8S_NAMESPACE}
+
+ echo; echo Get pods in namespace ${K8S_NAMESPACE} in context ${K8S_CONTEXT}...
+ kubectl get pods -o wide -n ${K8S_NAMESPACE}
+
+ done # SC
+
+done # MISSIONS
diff --git a/deployments/scripts/nos3.yaml b/deployments/scripts/nos3.yaml
new file mode 100644
index 000000000..40c627a63
--- /dev/null
+++ b/deployments/scripts/nos3.yaml
@@ -0,0 +1,249 @@
+metadata:
+ OU: myorg
+ ENVIRO: dev
+ TENANT: tenant
+ CONTEXT: missions # -exp must exist to match remote AWS EKS cluster
+ PURPOSE: -exp
+
+defaults:
+ spec:
+ HEALTHCHECK_PORT: &HEALTHCHECK_PORT
+ 60000
+ PATHS: &PATHS
+ BASE_DIR: &BASE_DIR
+ /builds/nos3
+ SIM_BIN: &SIM_BIN
+ /builds/nos3/sims/build/bin
+ SIMULATOR_BIN: &SIMULATOR_BIN
+ /builds/nos3/sims/build/bin/nos3-single-simulator
+ LOG_CONFIG: &LOG_CONFIG
+ /builds/nos3/sims/build/bin/sim_log_config.xml
+ # CFG_FILE: &CFG_FILE
+ # /builds/nos3/sims/build/bin/nos3-simulator.xml
+ SC_CFG_FILE: &SC_CFG_FILE
+ /builds/nos3/sims/build/bin/nos3-simulator.xml
+ GND_CFG_FILE: &GND_CFG_FILE
+ /builds/nos3/sims/build/bin/nos3-simulator.xml
+ IMAGE_REGISTRY: &IMAGE_REGISTRY
+ registry.appdat.jsc.nasa.gov
+ IMAGE_URI: &IMAGE_URI
+ registry.appdat.jsc.nasa.gov/ssmo/images/ssmo/nos3/nos3-base:20250217
+ container: &container
+ PATHS:
+ << : *PATHS
+ resources: &resources
+ limits: &limits
+ memory: "128Mi"
+ cpu: "100m"
+ requests: &requests
+ memory: "128Mi"
+ cpu: "100m"
+ nodes: &nodes
+ min_size: 1
+ max_size: 1
+ desired_size: 1
+ instance_types: m6i.xlarge
+ nodeSelector: ""
+ imagePullPolicy: IfNotPresent
+ components: &hw_components
+ camsim:
+ name: camsim
+ replicas: 1
+ container:
+ << : *container
+ generic-css-sim:
+ name: css
+ replicas: 1
+ container:
+ << : *container
+ generic-eps-sim:
+ name: eps
+ replicas: 1
+ container:
+ << : *container
+ generic-fss-sim:
+ name: fss
+ replicas: 1
+ container:
+ << : *container
+ gps:
+ name: gps
+ replicas: 1
+ container:
+ << : *container
+ generic-imu-sim:
+ name: imu
+ replicas: 1
+ container:
+ << : *container
+ generic-mag-sim:
+ name: mag
+ replicas: 1
+ container:
+ << : *container
+ generic-reactionwheel-sim0:
+ name: rw0
+ replicas: 1
+ container:
+ << : *container
+ generic-reactionwheel-sim1:
+ name: rw1
+ replicas: 1
+ container:
+ << : *container
+ generic-reactionwheel-sim2:
+ name: rw2
+ replicas: 1
+ container:
+ << : *container
+ generic-radio-sim:
+ name: radio_sim
+ replicas: 1
+ container:
+ PATHS:
+ << : *PATHS
+ resources:
+ limits:
+ memory: "128Mi"
+ cpu: "125m"
+ requests:
+ memory: "128Mi"
+ cpu: "125m"
+ host: radio_sim
+ network-alias: radio_sim
+ generic-star-tracker-sim:
+ name: start-tracker
+ replicas: 1
+ container:
+ << : *container
+ generic-torquer-sim:
+ name: torquer
+ replicas: 1
+ container:
+ << : *container
+ generic-thruster-sim:
+ name: thruster
+ replicas: 1
+ container:
+ << : *container
+ time:
+ name: time
+ replicas: 1
+ container:
+ << : *container
+ stdio-terminal:
+ name: stdio-terminal
+ replicas: 1
+ container:
+ << : *container
+ udp-terminal:
+ replicas: 2
+ name: udp-terminal
+ container:
+ << : *container
+ # nos_engine: & nos_engine
+ # container:
+ # << : *container
+ # truth42: &truth42
+ # container:
+ # << : *container
+ # flight_dynamics: &flight_dynamics
+ # fortytwo: &fortytwo
+ # container:
+ # << : *container
+ # encryption: &encryption
+ # cryptolib:
+ # container:
+ # << : *container
+
+projects:
+ nos3:
+ missions:
+ m01:
+ enabled: true
+ spacecraft:
+ sc01:
+ enabled: true
+ components:
+ fortytwo:
+ port: 30090
+ yamcs:
+ port: 8090
+ openmct:
+ port: 9000
+ sc02:
+ enabled: false
+ components:
+ fortytwo:
+ port: 30091
+ yamcs:
+ port: 8091
+ openmct:
+ port: 9001
+ m02:
+ enabled: false
+ spacecraft:
+ sc01:
+ enabled: true
+ components:
+ fortytwo:
+ port: 30092
+ yamcs:
+ port: 8092
+ openmct:
+ port: 9002
+ sc02:
+ enabled: false
+ components:
+ fortytwo:
+ port: 30093
+ yamcs:
+ port: 8093
+ openmct:
+ port: 9003
+
+# projects:
+# nos3:
+# missions:
+# - m01:
+# enabled: true
+# spacecraft:
+# - sc01:
+# enabled: true
+# components:
+# fortytwo:
+# name: fsw
+# replicas: 1
+# container:
+# << : *container
+# fsw:
+# name: fsw
+# replicas: 1
+# container:
+# << : *container
+# nos-engine-server:
+# name: nos-engine-server
+# replicas: 1
+# container:
+# << : *container
+# time:
+# name: time
+# replicas: 1
+# container:
+# << : *container
+# gps:
+# name: gps
+# replicas: 1
+# container:
+# << : *container
+# - m02:
+# enabled: false
+# spacecraft:
+# - sc01:
+# enabled: true
+# components:
+# << : *hw_components
+# - sc02:
+# enabled: false
+# components:
+# << : *hw_components
\ No newline at end of file
diff --git a/deployments/scripts/nos3_new.yml b/deployments/scripts/nos3_new.yml
new file mode 100644
index 000000000..50b46ec2a
--- /dev/null
+++ b/deployments/scripts/nos3_new.yml
@@ -0,0 +1,39 @@
+# cat /Users/hido/development//yaml_anchors_aliases.yml | yq '. | explode(.)'
+
+metadata:
+ OU: myorg
+ ENVIRO: dev
+ TENANT: tenant
+ CONTEXT: missions # -exp must exist to match remote AWS EKS cluster
+ PURPOSE: '-exp'
+
+defaults:
+ spec:
+ HEALTHCHECK_PORT: 60000
+ env: &env
+ IMAGE_REGISTRY: registry.appdat.jsc.nasa.gov
+ IMAGE_URI: registry.appdat.jsc.nasa.gov/ssmo/images/ssmo/nos3/nos3-base:20250217
+ resources: &resources
+ replicas: 1
+ limits:
+ memory: 128Mi
+ cpu: 100m
+ requests:
+ memory: 128Mi
+ cpu: 100m
+ components:
+ camsim1:
+ name: camsim1
+ resources:
+ <<: *resources
+ camsim2:
+ name: camsim2
+
+# missions:
+# - m01:
+# enabled: true
+# spacecraft:
+# - sc1:
+# enabled: true
+# components:
+# << : *components
\ No newline at end of file
diff --git a/deployments/scripts/sidecar.sh b/deployments/scripts/sidecar.sh
new file mode 100755
index 000000000..b052b0994
--- /dev/null
+++ b/deployments/scripts/sidecar.sh
@@ -0,0 +1,157 @@
+#!/usr/bin/env bash
+
+usage() {
+ cat <<-EOF
+
+ Usage: $0 [-h] [-d] [-P] [-s] [-p] [-i] [-c] [-t] [-R] [-w]
+
+ Purpose: a script to disable, enable links, and issue a command to a Yamcs server's instance on a specific processor.
+
+ Eg.:
+ $0 -h
+ $0 -d
+ $0 -P http -s localhost -p 8090 -i nos3
+ $0 -w
+ etc.
+
+ Required Arguments:
+
+ Options:
+ -h | --help help
+ -d | --defaults use default configurations. Other arguments will be ignored.
+ -P | --protocol , Default: http
+ -s | --server server's address, Default: localhost
+ -p | --port service's port, Default: 8090
+ -i | --instance instance on server, Default: nos3
+ -c | --command command to issue, Default: /CFS/CMD/CFE_ES_NOOP, invoke /CFS/CMD/TO_ENABLE_OUTPUT to enable telemetry outputs
+ -t | --tls_verify Default: False
+ -R | --processor Default: realtime
+ -w | --write Echo default values to stdout
+
+EOF
+}
+
+params="$(getopt -o :h,d,P:s:p:i:c:t:R,w -l 'help,defaults,protocol:,server:,port:,instance:,command:,tls_verify:,processor,write' --name "$(basename $0)" -- "$@")"
+
+# Check if getopt encountered an error
+if [ $? -ne 0 ]; then
+ echo
+ echo "ERROR: Invalid option or missing argument: $@" >&2
+ usage
+ exit 1
+fi
+
+if [ $# -eq 0 ]; then
+ echo
+ echo "ERROR: no arguments provided: $@" >&2
+ usage
+ exit 1
+fi
+
+eval set -- "$params"
+unset params
+
+# Default values
+PROTOCOL=http
+SERVER=localhost
+PORT=8090
+INSTANCE=nos3
+PROCESSOR=realtime
+COMMAND="/CFS/CMD/TO_ENABLE_OUTPUT"
+#COMMAND="/CFS/CMD/CFE_ES_NOOP"
+TLS_VERIFY=False
+
+while true; do
+ case "$1" in
+ -h|--help)
+ usage
+ exit 0
+ ;;
+ -d|--defaults)
+ echo; echo "[INFO] Will use default values. Other arguments will be ignored."
+ shift
+ break
+ ;;
+ -P|--protocol)
+ PROTOCOL="$2"
+ shift 2
+ ;;
+ -s|--server)
+ SERVER="$2"
+ shift 2
+ ;;
+ -p|--port)
+ PORT="$2"
+ shift 2
+ ;;
+ -i|--instance)
+ INSTANCE="$2"
+ shift 2
+ ;;
+ -c|--command)
+ COMMAND="$2"
+ shift 2
+ ;;
+ -t|--tls_verify)
+ TLS_VERIFY="$2"
+ shift 2
+ ;;
+ -w|--write)
+ echo
+ echo "[INFO] defaults are ${PROTOCOL}://${SERVER}:${PORT} on instance: $INSTANCE, with command: $COMMAND, using processor: $PROCESSOR"
+ echo
+ exit
+ ;;
+ --)
+ shift
+ break
+ ;;
+ *)
+ echo "Unrecognized option '$1'"
+ usage
+ exit
+ ;;
+ esac
+
+done
+
+echo
+echo "Attempting to connect to yamcs on:"
+echo " ${PROTOCOL}://${SERVER}:${PORT} on instance: $INSTANCE, with command: $COMMAND, using processor: $PROCESSOR"
+echo
+
+curl -k -X POST ${PROTOCOL}://${SERVER}:${PORT}/api/links/nos3/radio-in:disable
+curl -k -X POST ${PROTOCOL}://${SERVER}:${PORT}/api/links/nos3/radio-out:disable
+curl -k -X POST ${PROTOCOL}://${SERVER}:${PORT}/api/links/nos3/truth42-in:disable
+
+sleep 5
+
+curl -k -X POST ${PROTOCOL}://${SERVER}:${PORT}/api/links/nos3/radio-in:enable
+curl -k -X POST ${PROTOCOL}://${SERVER}:${PORT}/api/links/nos3/radio-out:enable
+curl -k -X POST ${PROTOCOL}://${SERVER}:${PORT}/api/links/nos3/truth42-in:enable
+
+pip3 install --break-system-packages --user --upgrade yamcs-client && \
+
+python3 </etc/apt/trusted.gpg.d/TurboVNC.gpg
+RUN wget -P /etc/apt/sources.list.d/ https://raw.githubusercontent.com/TurboVNC/repo/main/TurboVNC.list
+
+# virtualgl: https://virtualgl.org/
+RUN wget -q -O- https://packagecloud.io/dcommander/virtualgl/gpgkey | gpg --dearmor >/etc/apt/trusted.gpg.d/VirtualGL.gpg
+RUN wget -P /etc/apt/sources.list.d/ https://raw.githubusercontent.com/VirtualGL/repo/main/VirtualGL.list
+
+# Install virtualgl and turbovnc
+RUN apt-get update && \
+ apt-get install -y \
+ virtualgl \
+ turbovnc && \
+ apt-get clean && \
+ rm -rf /var/lib/apt/lists/*
+
+# Install and configure Display Manager(s) - https://fluxbox.org/
+RUN apt-get update && \
+ apt-get install -y \
+ fluxbox && \
+ apt-get clean && \
+ rm -rf /var/lib/apt/lists/*
+
+# TODO: need to figure out why this is needed
+RUN ln -sf /usr/share/xsessions/fluxbox.desktop /usr/share/xsessions/gnome.desktop
+
+# Install tools for git cloning and development, etc.
+RUN apt-get update && \
+ apt install -y curl git build-essential vim jq tree tmux htop bash-completion dos2unix netcat-traditional iputils-ping && \
+ apt-get clean && \
+ rm -rf /var/lib/apt/lists/*
+
+# Install https://taskfile.dev/ (Taskfile.yaml)
+RUN sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d
+
+# Install some useful .bashrc-isms
+# COPY .bashrc.local /root/.bashrc.local
+# RUN cat /root/.bashrc.local >> /root/.bashrc
+
+# VNC Config
+RUN mkdir -p ~/.vnc/
+RUN echo ${VNC_PASSWORD} | vncpasswd -f > ~/.vnc/passwd
+RUN chmod 0600 ~/.vnc/passwd
+RUN openssl req -x509 -nodes -newkey rsa:2048 -keyout ~/novnc.pem -out ~/novnc.pem -days 3650 -subj "/C=US/ST=NY/L=NY/O=NY/OU=NY/CN=NY emailAddress=email@example.com"
+
+################################################################################
+FROM x-vnc AS fortytwo
+################################################################################
+ARG GIT_URL
+ARG GIT_COMMIT
+
+ENV GIT_URL=${GIT_URL}
+ENV GIT_COMMIT=${GIT_COMMIT}
+
+# VNC Config
+RUN mkdir -p ~/.vnc/
+RUN echo ${VNC_PASSWORD} | vncpasswd -f > ~/.vnc/passwd
+RUN chmod 0600 ~/.vnc/passwd
+RUN openssl req -x509 -nodes -newkey rsa:2048 -keyout ~/novnc.pem -out ~/novnc.pem -days 3650 -subj "/C=US/ST=NY/L=NY/O=NY/OU=NY/CN=NY emailAddress=email@example.com"
+
+# Install 42's dependencies
+RUN apt-get update && \
+ apt-get -y install libglu1-mesa-dev freeglut3-dev mesa-common-dev libglfw3-dev && \
+ apt-get clean && \
+ rm -rf /var/lib/apt/lists/*
+
+# 42's MonteCarlo dependencies
+RUN apt-get update && \
+ apt-get -y install octave tshark && \
+ apt-get clean && \
+ rm -rf /var/lib/apt/lists/*
+
+# install Julia
+RUN curl -fsSL https://install.julialang.org | sh -s -- --yes
+
+# Clone the 42 repo for a specific branch
+
+RUN mkdir -p /opt/nasa-itc && \
+ cd /opt/nasa-itc && \
+ git clone --recurse-submodules -b ${GIT_COMMIT} -j4 ${GIT_URL} && \
+ cd ./42 &&\
+ make clean && \
+ make -j7
+
+COPY entrypoint.sh /entrypoint.sh
+
+CMD ["/entrypoint.sh"]
diff --git a/deployments/services/fortytwo/entrypoint.sh b/deployments/services/fortytwo/entrypoint.sh
new file mode 100755
index 000000000..48057b74a
--- /dev/null
+++ b/deployments/services/fortytwo/entrypoint.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+
+rm -f /tmp/.X1-lock || true
+rm -f /tmp/.X11-unix/X1 || true
+
+pkill -9 -f vncserver || true
+pkill -9 -f Xvnc || true
+pkill -9 -f websockify || true
+
+/opt/TurboVNC/bin/vncserver -securitytypes tlsnone,x509none,none && \
+ sleep 5 && \
+ websockify -D \
+ --web=/usr/share/novnc/ \
+ --cert=~/novnc.pem 80 localhost:5901
+
+export DISPLAY=${DISPLAY:-:1}
+export DIR=/opt/nasa-itc
+export GIT_FOLDER=${GIT_FOLDER}
+
+xterm &
+
+rm -rf /opt/nasa-itc/42/NO3InOut/{*.42,*.csv}
+
+if [ "$RECOMPILE" == "true" ]; then
+ cd /opt/nasa-itc/42 && \
+ git fetch && \
+ git checkout ${GIT_COMMIT} && \
+ git pull origin && \
+ git submodule update && \
+ make clean && \
+ make -j2
+fi
+
+STARTUP_FOLDER=${STARTUP_FOLDER:-NO3InOut}
+
+cd /opt/nasa-itc/42 && \
+ xterm -e "./42 ${STARTUP_FOLDER}" &
+
+echo "Started 42 with PID $!"
+
+tail -f /dev/null
diff --git a/deployments/services/fortytwo/kubernetes/configmap-args.yaml b/deployments/services/fortytwo/kubernetes/configmap-args.yaml
new file mode 100644
index 000000000..8b1378917
--- /dev/null
+++ b/deployments/services/fortytwo/kubernetes/configmap-args.yaml
@@ -0,0 +1 @@
+
diff --git a/deployments/services/fortytwo/kubernetes/configmap-env.yaml b/deployments/services/fortytwo/kubernetes/configmap-env.yaml
new file mode 100644
index 000000000..9b46aba67
--- /dev/null
+++ b/deployments/services/fortytwo/kubernetes/configmap-env.yaml
@@ -0,0 +1,62 @@
+apiVersion: v1
+data:
+ BASE_DIR: /opt/nasa-itc
+ COMPONENT_DIR: /home/nos3/builds/nos3/components
+ COMPOSE_PROJECT_NAME: nos3-m01-sc01
+ DEPLOYMENT_ENVIRO: ""
+ FORTYTWO_BASE_DIR: /opt/nasa-itc
+ FORTYTWO_DISPLAY: :1
+ FORTYTWO_GIT_COMMIT: nos3-main
+ FORTYTWO_GIT_FOLDER: "42"
+ FORTYTWO_GIT_URL: https://github.com/nasa-itc/42.git
+ FORTYTWO_LEAP_SECONDS: "37"
+ FORTYTWO_PORT: "30090"
+ FORTYTWO_RECOMPILE: "false"
+ FORTYTWO_SIM_DATE: '"01 06 2026"'
+ FORTYTWO_SIM_TIME: '"18 46 59.00"'
+ FORTYTWO_STARTUP_FOLDER: NOS3InOut
+ FORTYTWO_VNC_PASSWORD: c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2
+ FSW_SOFTWARE: cfs
+ GSW_SOFTWARE: yamcs
+ HEALTHCHECK_PORT: ""
+ HTTP_PROXY: ""
+ HTTPS_PROXY: ""
+ J2000_EPOCH_SECONDS: "946728000"
+ J2000_REFERENCE_DATETIME: '"2000-01-01 12:00:00 UTC"'
+ MAVEN_HTTPS_PROXY: '"--settings ./settings.xml"'
+ MAVEN_REPO_LOCAL: /home/nos3/.nos3/.m2
+ MAVEN_SETTINGS_FILE: ""
+ MISSION: m01
+ NO_PROXY: ""
+ NOS3_BASE_DIR: /home/nos3/.nos3
+ NOS3_CFG_PATH: ..
+ NOS3_DIR: /home/nos3/.nos3
+ NOS3_GIT_COMMIT: dev
+ NOS3_GIT_URL: https://github.com/nasa/nos3
+ NOS3_IMAGE_URI: docker.io/ivvitc/nos3-64:20251107
+ NOS3_MISSION_CFG_FILE: ../cfg/nos3-mission.xml
+ NOS3_USER: nos3
+ OPENMCT_GIT_COMMIT: master
+ OPENMCT_GIT_URL: https://github.com/akhenry/openmct-yamcs.git
+ OPENMCT_IMAGE_URI: docker.io/library/ubuntu:25.04
+ OPENMCT_NODE_VERSION: v21.7.3
+ OPENMCT_NVM_VERSION: v0.40.1
+ OPENMCT_PORT: "9000"
+ PROJECT_NAME: nos3-m01-sc01
+ SIM_T0_DATETIME: '"2026-01-06 18:46:29 UTC"'
+ SIM_T0_EPOCH_MILLISECONDS: "1767725219000"
+ SIM_T0_EPOCH_SECONDS: "1767725219"
+ SIM_TF_EPOCH_MILLISECONDS: "1767728819000"
+ SPACECRAFT: sc01
+ START_TIME: "820997219"
+ TIME_INTERVAL: "3600"
+ TIME_INTERVAL_MILLISECONDS: "3600000"
+ TIME_OFFSET_SECONDS: "+30"
+ YAMCS_DATA_DIR: /home/nos3/.nos3/yamcs/target/yamcs/yamcs-data
+ YAMCS_GIT_COMMIT: nos3-dev
+ YAMCS_GIT_URL: https://github.com/nasa-itc/nos3_yamcs_master.git
+ YAMCS_INSTANCES: nos3
+ YAMCS_PORT: "8090"
+kind: ConfigMap
+metadata:
+ name: nos3-m01-sc01-fortytwo
diff --git a/deployments/services/fortytwo/kubernetes/configmap-volumes.yaml b/deployments/services/fortytwo/kubernetes/configmap-volumes.yaml
new file mode 100644
index 000000000..45ff864a1
--- /dev/null
+++ b/deployments/services/fortytwo/kubernetes/configmap-volumes.yaml
@@ -0,0 +1,16 @@
+apiVersion: v1
+data:
+ entrypoint.sh: "#!/bin/bash\n\nrm -f /tmp/.X1-lock || true\nrm -f /tmp/.X11-unix/X1
+ || true\n\npkill -9 -f vncserver || true\npkill -9 -f Xvnc || true\npkill -9 -f
+ websockify || true\n\n/opt/TurboVNC/bin/vncserver -securitytypes tlsnone,x509none,none
+ && \\\n sleep 5 && \\\n websockify -D \\\n --web=/usr/share/novnc/ \\\n --cert=~/novnc.pem
+ 80 localhost:5901 \n \nexport DISPLAY=${DISPLAY:-:1}\nexport DIR=/opt/nasa-itc\nexport
+ GIT_FOLDER=${GIT_FOLDER}\n\nxterm &\n\nrm -rf /opt/nasa-itc/42/NO3InOut/{*.42,*.csv}
+ \n\nif [ \"$RECOMPILE\" == \"true\" ]; then\n cd /opt/nasa-itc/42 && \\\n git
+ fetch && \\\n git checkout ${GIT_COMMIT} && \\\n git pull origin && \\\n
+ \ git submodule update && \\\n make clean && \\\n make -j2\nfi\n\nSTARTUP_FOLDER=${STARTUP_FOLDER:-NO3InOut}\n\ncd
+ /opt/nasa-itc/42 && \\\n xterm -e \"./42 ${STARTUP_FOLDER}\" &\n\necho \"Started
+ 42 with PID $!\"\n\ntail -f /dev/null\n"
+kind: ConfigMap
+metadata:
+ name: nos3-m01-sc01-fortytwo-volumes
diff --git a/deployments/services/fortytwo/kubernetes/deployment.yaml b/deployments/services/fortytwo/kubernetes/deployment.yaml
new file mode 100644
index 000000000..e29eb0222
--- /dev/null
+++ b/deployments/services/fortytwo/kubernetes/deployment.yaml
@@ -0,0 +1,56 @@
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: nos3-m01-sc01-fortytwo
+ namespace: nos3-m01
+ labels:
+ app: nos3-m01-sc01-fortytwo
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: nos3-m01-sc01-fortytwo
+ template:
+ metadata:
+ labels:
+ app: nos3-m01-sc01-fortytwo
+ spec:
+ containers:
+ - args:
+ - /entrypoint.sh
+ env:
+ - name: COMPONENT_NAME
+ value: fortytwo
+ - name: DISPLAY
+ value: :1
+ image: ghcr.io/haisamido/nos3-base-fortytwo:dev
+ workingDir: /42
+ imagePullPolicy: Always
+ name: nos3-m01-sc01-fortytwo
+ ports:
+ - containerPort: 80
+ protocol: TCP
+ resources:
+ limits:
+ cpu: "3"
+ memory: "2048Mi"
+ requests:
+ cpu: "1"
+ memory: "2048Mi"
+ securityContext:
+ privileged: true
+ volumeMounts:
+ - mountPath: /entrypoint.sh
+ name: nos3-m01-sc01-fortytwo-volumes
+ subPath: entrypoint.sh
+ hostname: nos3-m01-sc01-fortytwo
+ restartPolicy: Always
+ volumes:
+ - configMap:
+ items:
+ - key: entrypoint.sh
+ path: entrypoint.sh
+ name: nos3-m01-sc01-fortytwo-volumes
+ defaultMode: 0555
+ name: "nos3-m01-sc01-fortytwo-volumes"
diff --git a/deployments/services/fortytwo/kubernetes/ingress.yaml b/deployments/services/fortytwo/kubernetes/ingress.yaml
new file mode 100644
index 000000000..8b1378917
--- /dev/null
+++ b/deployments/services/fortytwo/kubernetes/ingress.yaml
@@ -0,0 +1 @@
+
diff --git a/deployments/services/fortytwo/kubernetes/kustomization.yaml b/deployments/services/fortytwo/kubernetes/kustomization.yaml
new file mode 100644
index 000000000..64ba096b9
--- /dev/null
+++ b/deployments/services/fortytwo/kubernetes/kustomization.yaml
@@ -0,0 +1,11 @@
+---
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+resources:
+ - ./configmap-args.yaml
+ - ./configmap-env.yaml
+ - ./configmap-volumes.yaml
+ - ./deployment.yaml
+ - ./service.yaml
+ - ./ingress.yaml
+
diff --git a/deployments/services/fortytwo/kubernetes/service.yaml b/deployments/services/fortytwo/kubernetes/service.yaml
new file mode 100644
index 000000000..6a77faa15
--- /dev/null
+++ b/deployments/services/fortytwo/kubernetes/service.yaml
@@ -0,0 +1,15 @@
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: nos3-m01-sc01-fortytwo
+
+spec:
+ selector:
+ app: nos3-m01-sc01-fortytwo
+ ports:
+ - name: x-vnc
+ protocol: TCP
+ port: 80
+ targetPort: 80
+ type: ClusterIP
diff --git a/deployments/services/fortytwo/nos3.yaml b/deployments/services/fortytwo/nos3.yaml
new file mode 100644
index 000000000..4d98248f1
--- /dev/null
+++ b/deployments/services/fortytwo/nos3.yaml
@@ -0,0 +1,101 @@
+metadata:
+ OU: myorg
+ ENVIRO: dev
+ TENANT: tenant
+ CONTEXT: missions # -exp must exist to match remote AWS EKS cluster
+ PURPOSE: -exp
+
+defaults:
+ spec:
+ HEALTHCHECK_PORT: &HEALTHCHECK_PORT
+ 60000
+ PATHS: &PATHS
+ BASE_DIR: &BASE_DIR
+ /builds/nos3
+ SIM_BIN: &SIM_BIN
+ /builds/nos3/sims/build/bin
+ SIMULATOR_BIN: &SIMULATOR_BIN
+ /builds/nos3/sims/build/bin/nos3-single-simulator
+ LOG_CONFIG: &LOG_CONFIG
+ /builds/nos3/sims/build/bin/sim_log_config.xml
+ # CFG_FILE: &CFG_FILE
+ # /builds/nos3/sims/build/bin/nos3-simulator.xml
+ SC_CFG_FILE: &SC_CFG_FILE
+ /builds/nos3/sims/build/bin/nos3-simulator.xml
+ GND_CFG_FILE: &GND_CFG_FILE
+ /builds/nos3/sims/build/bin/nos3-simulator.xml
+ IMAGE_REGISTRY: &IMAGE_REGISTRY
+ ghcr.io
+ IMAGE_URI: &IMAGE_URI
+ ghcr.io/ericstoneking/42:latest
+ container: &container
+ PATHS:
+ << : *PATHS
+ resources: &resources
+ limits: &limits
+ memory: "128Mi"
+ cpu: "100m"
+ requests: &requests
+ memory: "128Mi"
+ cpu: "100m"
+ nodes: &nodes
+ min_size: 1
+ max_size: 1
+ desired_size: 1
+ instance_types: m6i.xlarge
+ nodeSelector: ""
+ imagePullPolicy: IfNotPresent
+ components: &hw_components
+ fortytwo:
+ name: fortytwo
+ host: fortytwo
+ replicas: 1
+ container:
+ PATHS:
+ BASE_DIR: &BASE_DIR
+ /builds/nos3
+ SIM_BIN: &SIM_BIN
+ /builds/nos3/sims/build/bin
+ SIMULATOR_BIN: &SIMULATOR_BIN
+ /builds/nos3/sims/build/bin/nos3-single-simulator
+ LOG_CONFIG: &LOG_CONFIG
+ /builds/nos3/sims/build/bin/sim_log_config.xml
+ # CFG_FILE: &CFG_FILE
+ # /builds/nos3/sims/build/bin/nos3-simulator.xml
+ SC_CFG_FILE: &SC_CFG_FILE
+ /builds/nos3/sims/build/bin/nos3-simulator.xml
+ GND_CFG_FILE: &GND_CFG_FILE
+ /builds/nos3/sims/build/bin/nos3-simulator.xml
+ IMAGE_REGISTRY: &IMAGE_REGISTRY
+ ghcr.io
+ IMAGE_URI: &IMAGE_URI
+ ghcr.io/ericstoneking/42:latest
+ resources: &resources
+ limits: &limits
+ memory: "128Mi"
+ cpu: "100m"
+ requests: &requests
+ memory: "128Mi"
+ cpu: "100m"
+
+
+missions:
+ - m01:
+ enabled: true
+ spacecraft:
+ - sc1:
+ enabled: true
+ components:
+ << : *hw_components
+
+ - m02:
+ enabled: false
+ spacecraft:
+ - sc1:
+ enabled: true
+ components:
+ << : *hw_components
+ - sc2:
+ enabled: false
+ components:
+ << : *hw_components
\ No newline at end of file
diff --git a/deployments/services/fsw/Dockerfile b/deployments/services/fsw/Dockerfile
new file mode 100644
index 000000000..270eda61f
--- /dev/null
+++ b/deployments/services/fsw/Dockerfile
@@ -0,0 +1,119 @@
+ARG REGISTRY_HOST=ghcr.io
+ARG IMAGE_USERNAME=haisamido
+ARG IMAGE_NAME=nos3-64
+ARG IMAGE_TAG=dev
+ARG IMAGE_URI=${REGISTRY_HOST}/${IMAGE_USERNAME}/${IMAGE_NAME}:${IMAGE_TAG}
+#------------------------------------------------------------------------------
+
+#------------------------------------------------------------------------------
+ARG GIT_URL=https://github.com/nasa/nos3
+ARG GIT_COMMIT=dev
+
+ARG NOS3_USER=nos3
+ARG FSW_SOFTWARE=cfs
+ARG GSW_SOFTWARE=yamcs
+
+#------------------------------------------------------------------------------
+FROM ${IMAGE_URI} AS nos3-base
+
+ARG DEBIAN_FRONTEND=noninteractive
+
+#------------------------------------------------------------------------------
+# Proxy configs
+#------------------------------------------------------------------------------
+ARG MAVEN_HTTPS_PROXY
+ARG HTTPS_PROXY
+ARG HTTP_PROXY
+ARG NO_PROXY
+ARG DEPLOYMENT_ENVIRO
+
+ENV MAVEN_HTTPS_PROXY=${MAVEN_HTTPS_PROXY}
+ENV HTTPS_PROXY=${HTTPS_PROXY}
+ENV HTTP_PROXY=${HTTP_PROXY}
+ENV NO_PROXY=${NO_PROXY}
+ENV DEPLOYMENT_ENVIRO=${DEPLOYMENT_ENVIRO}
+
+#------------------------------------------------------------------------------
+# Git Configs
+#------------------------------------------------------------------------------
+ARG GIT_URL
+ARG GIT_COMMIT
+
+ENV GIT_URL=${GIT_URL}
+ENV GIT_COMMIT=${GIT_COMMIT}
+
+#------------------------------------------------------------------------------
+# NOS3 Configs
+#------------------------------------------------------------------------------
+ARG NOS3_USER
+ARG FSW_SOFTWARE
+ARG GSW_SOFTWARE
+ARG GROUND_SOFTWARE
+ARG LD_LIBRARY_PATH
+
+ENV NOS3_USER=${NOS3_USER}
+ENV FSW_SOFTWARE=${FSW_SOFTWARE}
+ENV GSW_SOFTWARE=${GSW_SOFTWARE}
+ENV GROUND_SOFTWARE=${GSW_SOFTWARE}
+ENV LD_LIBRARY_PATH=${LD_LIBRARY_PATH}
+
+#------------------------------------------------------------------------------
+
+#------------------------------------------------------------------------------
+# Tools
+#------------------------------------------------------------------------------
+RUN apt-get update && \
+ apt-get install -y sudo git curl vim make cmake tmux tree python3 pip && \
+ apt-get install -y iputils-ping dnsutils lsof net-tools tshark jq && \
+ apt-get install -y libgcrypt20-dev && \
+ apt-get clean && \
+ rm -rf /var/lib/apt/lists/*
+
+RUN git config --global http.sslVerify false
+
+#------------------------------------------------------------------------------
+# Create a new user named ${NOS3_USER} with a home directory
+#------------------------------------------------------------------------------
+RUN useradd -m ${NOS3_USER}
+RUN adduser ${NOS3_USER} sudo
+RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
+
+# Switch to the newly created user
+USER ${NOS3_USER}
+
+# Create builds directory
+RUN mkdir -p /home/${NOS3_USER}/builds/
+
+WORKDIR /home/${NOS3_USER}/builds/
+
+#------------------------------------------------------------------------------
+# Clone ${GIT_URL}
+#------------------------------------------------------------------------------
+RUN git clone --recurse-submodules -b ${GIT_COMMIT} -j4 ${GIT_URL}
+
+WORKDIR /home/${NOS3_USER}/builds/nos3
+
+RUN chown -R ${NOS3_USER}:${NOS3_USER} /home/${NOS3_USER}
+
+# commented printf(CRYPTO_PROMPT) because it fills up volume while the cryptolib is running
+COPY components/cryptolib/standalone/standalone.h ./components/cryptolib/support/standalone/standalone.h
+COPY components/cryptolib/standalone/standalone.c ./components/cryptolib/support/standalone/standalone.c
+COPY scripts/cfg/prepare.sh ./scripts/cfg/prepare.sh
+
+RUN make -j6 clean
+RUN make -j6 prep
+RUN make -j6 config
+RUN make -j6 build-cryptolib
+
+RUN make -j6 -e FSW_SOFTWARE=${FSW_SOFTWARE} build-fsw
+RUN make -j6 build-sim
+#RUN make -j6 build-test
+
+#------------------------------------------------------------------------------
+# Install the relevant GSW Software
+#------------------------------------------------------------------------------
+RUN ./scripts/gsw/gsw_${GSW_SOFTWARE}_build.sh
+
+COPY entrypoint.sh /entrypoint.sh
+
+CMD ["/entrypoint.sh"]
\ No newline at end of file
diff --git a/deployments/services/fsw/components/cryptolib/standalone/standalone.c b/deployments/services/fsw/components/cryptolib/standalone/standalone.c
new file mode 100755
index 000000000..b9b189d5a
--- /dev/null
+++ b/deployments/services/fsw/components/cryptolib/standalone/standalone.c
@@ -0,0 +1,1072 @@
+/* Copyright (C) 2009 - 2022 National Aeronautics and Space Administration.
+ All Foreign Rights are Reserved to the U.S. Government.
+
+ This software is provided "as is" without any warranty of any kind, either expressed, implied, or statutory,
+ including, but not limited to, any warranty that the software will conform to specifications, any implied warranties
+ of merchantability, fitness for a particular purpose, and freedom from infringement, and any warranty that the
+ documentation will conform to the program, or any warranty that the software will be error free.
+
+ In no event shall NASA be liable for any damages, including, but not limited to direct, indirect, special or
+ consequential damages, arising out of, resulting from, or in any way connected with the software or its
+ documentation, whether or not based upon warranty, contract, tort or otherwise, and whether or not loss was sustained
+ from, or arose out of the results of, or use of, the software, documentation or services provided hereunder.
+
+ ITC Team
+ NASA IV&V
+ jstar-development-team@mail.nasa.gov
+*/
+
+/*******************************************************************************
+** Standalone CryptoLib Implementation
+** UDP interfaces to apply / process each frame type and return the result.
+*******************************************************************************/
+
+#include "standalone.h"
+
+/*
+** Global Variables
+*/
+#define DYNAMIC_LENGTHS 1
+
+static volatile uint8_t keepRunning = CRYPTO_LIB_SUCCESS;
+static volatile uint8_t tc_seq_num = 0;
+static volatile uint8_t tc_vcid = CRYPTO_STANDALONE_FRAMING_VCID;
+static volatile uint8_t tc_debug = 1;
+static volatile uint8_t tm_debug = 0;
+static volatile uint8_t crypto_use_tcp = STANDALONE_TCP ? 1 : 0;
+
+/*
+** Functions
+*/
+int32_t crypto_standalone_check_number_arguments(int actual, int expected)
+{
+ int32_t status = CRYPTO_LIB_SUCCESS;
+ if (actual != expected)
+ {
+ status = CRYPTO_LIB_ERROR;
+ printf("Invalid command format or number of arguments, type 'help' for more info\n");
+ }
+ return status;
+}
+
+void crypto_standalone_to_lower(char *str)
+{
+ char *ptr = str;
+ while (*ptr)
+ {
+ *ptr = tolower((unsigned char)*ptr);
+ ptr++;
+ }
+ return;
+}
+
+void crypto_standalone_print_help(void)
+{
+ printf(CRYPTO_PROMPT "command [args]\n"
+ "----------------------------------------------------------------------\n"
+ "exit - Exit app \n"
+ "help - Display help \n"
+ "noop - No operation command to device \n"
+ "reset - Reset CryptoLib \n"
+ "active - Displays all operational SAs \n"
+ "tc - Toggle TC debug prints \n"
+ "tm - Toggle TM debug prints \n"
+ "vcid # - Change active TC virtual channel \n"
+ "\n");
+}
+
+int32_t crypto_standalone_get_command(const char *str)
+{
+ int32_t status = CRYPTO_CMD_UNKNOWN;
+ char lcmd[CRYPTO_MAX_INPUT_TOKEN_SIZE];
+
+ strncpy(lcmd, str, CRYPTO_MAX_INPUT_TOKEN_SIZE);
+ crypto_standalone_to_lower(lcmd);
+
+ if (strcmp(lcmd, "help") == 0)
+ {
+ status = CRYPTO_CMD_HELP;
+ }
+ else if (strcmp(lcmd, "exit") == 0)
+ {
+ status = CRYPTO_CMD_EXIT;
+ }
+ else if (strcmp(lcmd, "noop") == 0)
+ {
+ status = CRYPTO_CMD_NOOP;
+ }
+ else if (strcmp(lcmd, "reset") == 0)
+ {
+ status = CRYPTO_CMD_RESET;
+ }
+ else if (strcmp(lcmd, "vcid") == 0)
+ {
+ status = CRYPTO_CMD_VCID;
+ }
+ else if (strcmp(lcmd, "tc") == 0)
+ {
+ status = CRYPTO_CMD_TC_DEBUG;
+ }
+ else if (strcmp(lcmd, "tm") == 0)
+ {
+ status = CRYPTO_CMD_TM_DEBUG;
+ }
+ else if (strcmp(lcmd, "active") == 0)
+ {
+ status = CRYPTO_CMD_ACTIVE;
+ }
+ return status;
+}
+
+int32_t crypto_standalone_process_command(int32_t cc, int32_t num_tokens, char *tokens)
+{
+ int32_t status = CRYPTO_LIB_SUCCESS;
+
+ /* Process command */
+ switch (cc)
+ {
+ case CRYPTO_CMD_HELP:
+ crypto_standalone_print_help();
+ break;
+
+ case CRYPTO_CMD_EXIT:
+ keepRunning = CRYPTO_LIB_ERROR;
+ break;
+
+ case CRYPTO_CMD_NOOP:
+ if (crypto_standalone_check_number_arguments(num_tokens, 0) == CRYPTO_LIB_SUCCESS)
+ {
+ printf("NOOP command success\n");
+ }
+ break;
+
+ case CRYPTO_CMD_RESET:
+ if (crypto_standalone_check_number_arguments(num_tokens, 0) == CRYPTO_LIB_SUCCESS)
+ {
+ status = crypto_reset();
+ printf("Reset command received\n");
+ }
+ break;
+
+ case CRYPTO_CMD_VCID:
+ if (crypto_standalone_check_number_arguments(num_tokens, 1) == CRYPTO_LIB_SUCCESS)
+ {
+ uint8_t vcid = (uint8_t)atoi(&tokens[0]);
+ /* Confirm new VCID valid */
+ if (vcid < 64)
+ {
+ SaInterface sa_if = get_sa_interface_inmemory();
+ SecurityAssociation_t *test_association = NULL;
+ int32_t status = CRYPTO_LIB_SUCCESS;
+
+ status = sa_if->sa_get_operational_sa_from_gvcid(0, SCID, vcid, 0, &test_association);
+ if (status == CRYPTO_LIB_SUCCESS)
+ {
+ Crypto_saPrint(test_association);
+ }
+ printf("Get_SA_Status: %d\n", status);
+ if ((status == CRYPTO_LIB_SUCCESS) && (test_association->sa_state == SA_OPERATIONAL) &&
+ (test_association->gvcid_blk.mapid == TYPE_TC) && (test_association->gvcid_blk.scid == SCID))
+ {
+ tc_vcid = vcid;
+ printf("Changed active virtual channel (VCID) to %d \n", tc_vcid);
+ }
+ else
+ {
+ printf("Error - virtual channel (VCID) %d is invalid! Sticking with prior vcid %d \n", vcid,
+ tc_vcid);
+ status = CRYPTO_LIB_SUCCESS;
+ }
+ }
+ else
+ {
+ printf("Error - virtual channel (VCID) %d must be less than 64! Sticking with prior vcid %d \n",
+ vcid, tc_vcid);
+ }
+ }
+ break;
+
+ case CRYPTO_CMD_TC_DEBUG:
+ if (crypto_standalone_check_number_arguments(num_tokens, 0) == CRYPTO_LIB_SUCCESS)
+ {
+ if (tc_debug == 0)
+ {
+ tc_debug = 1;
+ printf("Enabled TC debug prints! \n");
+ }
+ else
+ {
+ tc_debug = 0;
+ printf("Disabled TC debug prints! \n");
+ }
+ }
+ break;
+
+ case CRYPTO_CMD_TM_DEBUG:
+ if (crypto_standalone_check_number_arguments(num_tokens, 0) == CRYPTO_LIB_SUCCESS)
+ {
+ if (tm_debug == 0)
+ {
+ tm_debug = 1;
+ printf("Enabled TM debug prints! \n");
+ }
+ else
+ {
+ tm_debug = 0;
+ printf("Disabled TM debug prints! \n");
+ }
+ }
+ break;
+
+ case CRYPTO_CMD_ACTIVE:
+ if (crypto_standalone_check_number_arguments(num_tokens, 0) == CRYPTO_LIB_SUCCESS)
+ {
+ SaInterface sa_if = get_sa_interface_inmemory();
+ SecurityAssociation_t *test_association = NULL;
+
+ printf("Active SAs: \n\t");
+ for (int i = 0; i < NUM_SA; i++)
+ {
+ sa_if->sa_get_from_spi(i, &test_association);
+ if (test_association->sa_state == SA_OPERATIONAL)
+ {
+ if (i < 5)
+ {
+ printf("TC - ");
+ }
+ if (i > 4 && i < 9)
+ {
+ printf("TM - ");
+ }
+ if (i > 8 && i < 13)
+ {
+ printf("AOS - ");
+ }
+ if (i == 63)
+ {
+ printf("ExProc - ");
+ }
+
+ printf("SPI %d - VCID %d - EST %d - AST %d\n\t", i, test_association->gvcid_blk.vcid,
+ test_association->est, test_association->ast);
+ }
+ }
+ printf("\n");
+ }
+ break;
+
+ default:
+ // printf("Invalid command format, type 'help' for more info\n");
+ // status = CRYPTO_LIB_ERROR;
+ break;
+ }
+
+ return status;
+}
+
+int32_t crypto_host_to_ip(const char *hostname, char *ip)
+{
+ struct addrinfo hints, *res, *p;
+ int status;
+ void *addr;
+
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = AF_INET; // Uses IPV4 only. AF_UNSPEC for IPV6 Support
+ hints.ai_socktype = SOCK_STREAM;
+
+ if ((status = getaddrinfo(hostname, NULL, &hints, &res)) != 0)
+ {
+ return 1;
+ }
+
+ for (p = res; p != NULL; p = p->ai_next)
+ {
+ struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
+ addr = &(ipv4->sin_addr);
+
+ // Convert IP to String
+ if (inet_ntop(p->ai_family, addr, ip, INET_ADDRSTRLEN) == NULL)
+ {
+ freeaddrinfo(res);
+ return 1;
+ }
+
+ freeaddrinfo(res);
+ return 0; // IP Found
+ }
+ freeaddrinfo(res);
+ return 1; // IP NOT Found
+}
+
+int32_t crypto_standalone_socket_init(udp_info_t *sock, int32_t port, uint8_t bind_sock, int connection)
+{
+ int status = CRYPTO_LIB_SUCCESS;
+ int optval;
+ socklen_t optlen;
+
+ sock->port = port;
+
+ if (connection == 1)
+ {
+ /* Creating TCP socket */
+ sock->sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
+
+ if (sock->sockfd == -1)
+ {
+ printf("tcp_init: Socket create error on port %d\n", sock->port);
+ return CRYPTO_LIB_ERROR;
+ }
+
+ /* Determine IP */
+ sock->saddr.sin_family = AF_INET;
+ if (inet_addr(sock->ip_address) != INADDR_NONE)
+ {
+ sock->saddr.sin_addr.s_addr = inet_addr(sock->ip_address);
+ }
+ else
+ {
+ char ip[16];
+ int check = crypto_host_to_ip(sock->ip_address, ip);
+ if (check == 0)
+ {
+ sock->saddr.sin_addr.s_addr = inet_addr(ip);
+ }
+ else
+ {
+ printf("socket_init: Failed to resolve hostname '%s'\n", sock->ip_address);
+ return CRYPTO_LIB_ERROR;
+ }
+ }
+ sock->saddr.sin_port = htons(sock->port);
+ }
+ else
+ {
+ /* Create UDP socket */
+ sock->sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
+ if (sock->sockfd == -1)
+ {
+ printf("udp_init: Socket create error port %d \n", sock->port);
+ }
+
+ /* Determine IP */
+ sock->saddr.sin_family = AF_INET;
+ if (inet_addr(sock->ip_address) != INADDR_NONE)
+ {
+ sock->saddr.sin_addr.s_addr = inet_addr(sock->ip_address);
+ }
+ else
+ {
+ char ip[16];
+ int check = crypto_host_to_ip(sock->ip_address, ip);
+ if (check == 0)
+ {
+ sock->saddr.sin_addr.s_addr = inet_addr(ip);
+ }
+ }
+ sock->saddr.sin_port = htons(sock->port);
+ }
+
+ if (crypto_use_tcp && ((sock->port == TC_APPLY_FWD_PORT || sock->port == TM_PROCESS_PORT)))
+ {
+ if (bind_sock != 0)
+ {
+ // TCP server: bind, listen, accept
+ if (bind(sock->sockfd, (struct sockaddr *)&sock->saddr, sizeof(sock->saddr)) != 0)
+ {
+ printf("tcp_init: Bind failed on port %d\n", sock->port);
+ return CRYPTO_LIB_ERROR;
+ }
+
+ if (listen(sock->sockfd, 1) != 0)
+ {
+ printf("tcp_init: Listen failed on port %d\n", sock->port);
+ return CRYPTO_LIB_ERROR;
+ }
+
+ int clientfd = accept(sock->sockfd, NULL, NULL);
+ if (clientfd < 0)
+ {
+ printf("tcp_init: Accept failed on port %d\n", sock->port);
+ return CRYPTO_LIB_ERROR;
+ }
+
+ // Replace listener with connected client socket
+ // close(sock->sockfd); //may be needed
+ sock->sockfd = clientfd;
+ }
+ else
+ {
+ // TCP client: connect
+ if (connect(sock->sockfd, (struct sockaddr *)&sock->saddr, sizeof(sock->saddr)) < 0)
+ {
+ printf("tcp_init: Connect failed to %s:%d\n", sock->ip_address, sock->port);
+ return CRYPTO_LIB_ERROR;
+ }
+ }
+ }
+ else
+ {
+ // UDP: bind only if needed
+ if (bind_sock == 0 && sock->port != TM_PROCESS_FWD_PORT && sock->port != TC_APPLY_FWD_PORT)
+ {
+ status = bind(sock->sockfd, (struct sockaddr *)&sock->saddr, sizeof(sock->saddr));
+ if (status != 0)
+ {
+ perror("bind");
+
+ printf("udp_init: Bind failed on port %d\n", sock->port);
+ return CRYPTO_LIB_ERROR;
+ }
+ // }
+ }
+ else
+ {
+ if (crypto_use_tcp == 0 && bind_sock == 1 && sock->port == TM_PROCESS_PORT)
+ {
+ status = bind(sock->sockfd, (struct sockaddr *)&sock->saddr, sizeof(sock->saddr));
+ if (status != 0)
+ {
+ perror("bind");
+
+ printf("udp_init: Bind failed on port %d\n", sock->port);
+ return CRYPTO_LIB_ERROR;
+ }
+ }
+ }
+ }
+
+ // Keep-alive socket option (not harmful for UDP, useful for TCP)
+ optval = 1;
+ optlen = sizeof(optval);
+ setsockopt(sock->sockfd, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen);
+
+ return status;
+}
+
+int32_t crypto_reset(void)
+{
+ int32_t status;
+
+ status = Crypto_Shutdown();
+ if (status != CRYPTO_LIB_SUCCESS)
+ {
+ printf("CryptoLib initialization failed with error %d \n", status);
+ }
+
+ status = Crypto_SC_Init();
+ if (status != CRYPTO_LIB_SUCCESS)
+ {
+ printf("CryptoLib initialization failed with error %d \n", status);
+ }
+
+ return status;
+}
+
+void crypto_standalone_tc_frame(uint8_t *in_data, uint16_t in_length, uint8_t *out_data, uint16_t *out_length)
+{
+ /* TC Length */
+ if (DYNAMIC_LENGTHS)
+ {
+ uint8_t segment_hdr_len = 1;
+ uint8_t fecf_len = tc_current_managed_parameters_struct.has_fecf ? 2 : 0;
+
+ *out_length = TC_FRAME_HEADER_SIZE + segment_hdr_len + in_length + fecf_len;
+ }
+ else
+ {
+ *out_length = CRYPTO_STANDALONE_FRAMING_TC_DATA_LEN + 6;
+ }
+
+ /* TC Header */
+ out_data[0] = 0x20;
+ out_data[1] = CRYPTO_STANDALONE_FRAMING_SCID;
+ out_data[2] = ((tc_vcid << 2) & 0xFC) | (((*out_length - 1) >> 8) & 0x03);
+ out_data[3] = (*out_length - 1) & 0xFF;
+ out_data[4] = tc_seq_num++;
+
+ /* Segement Header */
+ out_data[5] = 0xC0;
+
+ /* SDLS Header */
+
+ /* TC Data */
+ memcpy(&out_data[6], in_data, in_length);
+
+ /* SDLS Trailer */
+}
+
+void *crypto_standalone_tc_apply(void *socks)
+{
+ int32_t status = CRYPTO_LIB_SUCCESS;
+ udp_interface_t *tc_socks = (udp_interface_t *)socks;
+ udp_info_t *tc_read_sock = &tc_socks->read;
+ udp_info_t *tc_write_sock = &tc_socks->write;
+
+ uint8_t tc_apply_in[TC_MAX_FRAME_SIZE];
+ uint16_t tc_in_len = 0;
+ uint8_t *tc_out_ptr;
+ uint16_t tc_out_len = 0;
+
+#ifdef CRYPTO_STANDALONE_HANDLE_FRAMING
+ uint8_t tc_framed[TC_MAX_FRAME_SIZE] = {0};
+#endif
+
+ int sockaddr_size = sizeof(struct sockaddr_in);
+
+ /* Prepare */
+ memset(tc_apply_in, 0x00, sizeof(tc_apply_in));
+
+ while (keepRunning == CRYPTO_LIB_SUCCESS)
+ {
+ // /* Receive */
+ status = recvfrom(tc_read_sock->sockfd, tc_apply_in, sizeof(tc_apply_in), 0,
+ (struct sockaddr *)&tc_read_sock->ip_address, (socklen_t *)&sockaddr_size);
+ if (status != -1)
+ {
+ tc_in_len = status;
+ if (tc_debug == 1)
+ {
+ printf("crypto_standalone_tc_apply - received[%d]: 0x", tc_in_len);
+ for (int i = 0; i < status; i++)
+ {
+ printf("%02x", tc_apply_in[i]);
+ }
+ printf("\n");
+ }
+
+/* Frame */
+#ifdef CRYPTO_STANDALONE_HANDLE_FRAMING
+ crypto_standalone_tc_frame(tc_apply_in, tc_in_len, tc_framed, &tc_out_len);
+ memcpy(tc_apply_in, tc_framed, tc_out_len);
+ tc_in_len = tc_out_len;
+ tc_out_len = 0;
+ if (tc_debug == 1)
+ {
+ printf("crypto_standalone_tc_apply - framed[%d]: 0x", tc_in_len);
+ for (int i = 0; i < tc_in_len; i++)
+ {
+ printf("%02x", tc_apply_in[i]);
+ }
+ printf("\n");
+ }
+#endif
+
+ /* Process */
+ status = Crypto_TC_ApplySecurity(tc_apply_in, tc_in_len, &tc_out_ptr, &tc_out_len);
+ if (status == CRYPTO_LIB_SUCCESS)
+ {
+ if (tc_debug == 1)
+ {
+ printf("crypto_standalone_tc_apply - status = %d, encrypted[%d]: 0x", status, tc_out_len);
+ for (int i = 0; i < tc_out_len; i++)
+ {
+ printf("%02x", tc_out_ptr[i]);
+ }
+ printf("\n");
+ }
+ // printf("About to write to port %d!\n", tc_write_sock->port);
+ /* Reply */
+ if (crypto_use_tcp)
+ {
+ status = send(tc_write_sock->sockfd, tc_out_ptr, tc_out_len, 0);
+ }
+ else
+ {
+ status = sendto(tc_write_sock->sockfd, tc_out_ptr, tc_out_len, 0,
+ (struct sockaddr *)&tc_write_sock->saddr, sizeof(tc_write_sock->saddr));
+ }
+ if ((status == -1) || (status != tc_out_len))
+ {
+ printf("crypto_standalone_tc_apply - Reply error %d \n", status);
+ }
+ // printf("Allegedly wrote %d bytes to port %d!\n", tc_out_len, tc_write_sock->port);
+ }
+ else
+ {
+ printf("crypto_standalone_tc_apply - ApplySecurity error %d \n", status);
+ }
+
+ /* Reset */
+ memset(tc_apply_in, 0x00, sizeof(tc_apply_in));
+ memset(tc_framed, 0x00, sizeof(tc_framed));
+ tc_in_len = 0;
+ tc_out_len = 0;
+ if (!tc_out_ptr)
+ free(tc_out_ptr);
+ if (tc_debug == 1)
+ {
+#ifdef CRYPTO_STANDALONE_TC_APPLY_DEBUG
+ printf("\n");
+#endif
+ }
+ }
+
+ /* Delay */
+ usleep(100);
+ }
+ close(tc_read_sock->sockfd);
+ close(tc_write_sock->sockfd);
+ return tc_read_sock;
+}
+
+void crypto_standalone_tm_frame(uint8_t *in_data, uint16_t in_length, uint8_t *out_data, uint16_t *out_length,
+ uint16_t spi)
+{
+ SaInterface sa_if = get_sa_interface_inmemory();
+ SecurityAssociation_t *sa_ptr = NULL;
+ int32_t status = CRYPTO_LIB_SUCCESS;
+
+ status = sa_if->sa_get_from_spi(spi, &sa_ptr);
+ if (status != CRYPTO_LIB_SUCCESS)
+ {
+ printf("WARNING - SA IS NULL!\n");
+ }
+
+ // Calculate security headers and trailers
+ uint8_t header_length =
+ TM_PRI_HDR_LENGTH + SDLS_SPI_LENGTH + sa_ptr->shivf_len + sa_ptr->shplf_len + sa_ptr->shsnf_len;
+
+ uint8_t trailer_length = sa_ptr->stmacf_len;
+ if (tm_current_managed_parameters_struct.has_fecf == TM_HAS_FECF)
+ {
+ trailer_length += 2;
+ }
+ if (tm_current_managed_parameters_struct.has_ocf == TM_HAS_OCF)
+ {
+ trailer_length += 4;
+ }
+
+ /* TM Length */
+ *out_length = (uint16_t)in_length - header_length - trailer_length;
+
+ /* TM Header */
+ memcpy(out_data, &in_data[header_length], in_length - header_length - trailer_length);
+}
+
+void crypto_standalone_tm_debug_recv(int32_t status, int tm_process_len, uint8_t *tm_process_in)
+{
+ if (tm_debug == 1)
+ {
+ printf("crypto_standalone_tm_process - received[%d]: 0x", tm_process_len);
+ for (int i = 0; i < status; i++)
+ {
+ printf("%02x", tm_process_in[i]);
+ }
+ printf("\n");
+ }
+}
+
+void crypto_standalone_tm_debug_process(uint8_t *tm_process_in)
+{
+ if (tm_debug == 1)
+ {
+ printf("Printing first bytes of Tf Pri Hdr:\n\t");
+ for (int i = 0; i < 6; i++)
+ {
+ printf("%02X", *(tm_process_in + 4 + i));
+ }
+ printf("\n");
+ printf("Processing frame WITH ASM...\n");
+ }
+}
+
+void crypto_standalone_spp_telem_or_idle(int32_t *status, uint8_t *tm_ptr, uint16_t *spp_len, udp_interface_t *tm_socks,
+ int *tm_process_len)
+{
+ udp_info_t *tm_write_sock = &tm_socks->write;
+
+ if ((tm_ptr[0] == 0x08) || (tm_ptr[0] == 0x09) || ((tm_ptr[0] == 0x07) && (tm_ptr[1] == 0xff)) ||
+ (tm_ptr[0] == 0x0F && tm_ptr[1] == 0xFD))
+ {
+ *spp_len = (((0xFFFF & tm_ptr[4]) << 8) | tm_ptr[5]) + 7;
+#ifdef CRYPTO_STANDALONE_TM_PROCESS_DEBUG
+ printf("crypto_standalone_tm_process - SPP[%d]: 0x", *spp_len);
+ for (int i = 0; i < *spp_len; i++)
+ {
+ printf("%02x", tm_ptr[i]);
+ }
+ printf("\n");
+#endif
+ // Send all SPP telemetry packets
+ // 0x09 for HK/Device TLM Packets (Generic Components)
+ // 0x0FFD = CFDP
+ if (tm_ptr[0] == 0x08 || tm_ptr[0] == 0x09 || (tm_ptr[0] == 0x0f && tm_ptr[1] == 0xfd))
+ {
+ *status = sendto(tm_write_sock->sockfd, tm_ptr, *spp_len, 0, (struct sockaddr *)&tm_write_sock->saddr,
+ sizeof(tm_write_sock->saddr));
+ }
+ // Only send idle packets if configured to do so
+ else
+ {
+#ifdef CRYPTO_STANDALONE_DISCARD_IDLE_PACKETS
+ // Don't forward idle packets
+ *status = *spp_len;
+#else
+ *status = sendto(tm_write_sock->sockfd, tm_ptr, *spp_len, 0, (struct sockaddr *)&tm_write_sock->saddr,
+ sizeof(tm_write_sock->saddr));
+#endif
+ }
+
+ // Check status
+ if ((*status == -1) || (*status != *spp_len))
+ {
+ printf("crypto_standalone_tm_process - Reply error %d \n", *status);
+ }
+
+ *tm_process_len -= *spp_len;
+ }
+ else if ((tm_ptr[0] == 0xFF && tm_ptr[1] == 0x48) || (tm_ptr[0] == 0x00 && tm_ptr[1] == 0x00) ||
+ (tm_ptr[0] == 0x02 && tm_ptr[1] == 0x00) || (tm_ptr[0] == 0xFF && tm_ptr[1] == 0xFF))
+ {
+ // TODO: Why 0x0200?
+ // Idle Frame
+ // Idle Frame is entire length of remaining data
+#ifdef CRYPTO_STANDALONE_DISCARD_IDLE_FRAMES
+ // Don't forward idle frame
+ *status = *spp_len;
+#else
+ *status = sendto(tm_write_sock->sockfd, tm_ptr, *spp_len, 0, (struct sockaddr *)&tm_write_sock->saddr,
+ sizeof(tm_write_sock->saddr));
+ if ((*status == -1) || (*status != *spp_len))
+ {
+ printf("crypto_standalone_tm_process - Reply error %d \n", *status);
+ }
+#endif
+ *tm_process_len = 0;
+ }
+ else
+ {
+ printf("crypto_standalone_tm_process - SPP loop error, expected idle packet or frame! tm_ptr = 0x%02x%02x \n",
+ tm_ptr[0], tm_ptr[1]);
+ *tm_process_len = 0;
+ }
+}
+
+void *crypto_standalone_tm_process(void *socks)
+{
+ int32_t status = CRYPTO_LIB_SUCCESS;
+ udp_interface_t *tm_socks = (udp_interface_t *)socks;
+ udp_info_t *tm_read_sock = &tm_socks->read;
+ udp_info_t *tm_write_sock = &tm_socks->write;
+
+ uint8_t tm_process_in[TM_CADU_SIZE]; // Accounts for ASM automatically based on #def
+ int tm_process_len = 0;
+ uint16_t spp_len = 0;
+ uint8_t *tm_ptr;
+ uint16_t tm_out_len = 0;
+
+#ifdef CRYPTO_STANDALONE_HANDLE_FRAMING
+ uint8_t tm_framed[TM_CADU_SIZE];
+ uint16_t tm_framed_len = 0;
+#endif
+
+ int sockaddr_size = sizeof(struct sockaddr_in);
+
+ while (keepRunning == CRYPTO_LIB_SUCCESS)
+ {
+ /* Receive */
+ if (crypto_use_tcp)
+ {
+ status = recv(tm_read_sock->sockfd, tm_process_in, sizeof(tm_process_in), 0);
+ if (status == -1)
+ {
+ printf(" Problem with recv TCP tm_proccess: status = %d \n", status);
+ }
+ }
+ else
+ {
+ status = recvfrom(tm_read_sock->sockfd, tm_process_in, sizeof(tm_process_in), 0,
+ (struct sockaddr *)&tm_read_sock->ip_address, (socklen_t *)&sockaddr_size);
+ }
+ if (status != -1)
+ {
+ tm_process_len = status;
+ /* Receive */
+ crypto_standalone_tm_debug_recv(status, tm_process_len, tm_process_in);
+ /* Process */
+#ifdef TM_CADU_HAS_ASM
+ // Process Security skipping prepended ASM
+ crypto_standalone_tm_debug_process(tm_process_in);
+ // Account for ASM length
+ if (tm_process_in[4] == 0x40)
+ {
+ status = Crypto_AOS_ProcessSecurity(tm_process_in + 4, (const uint16_t)tm_process_len - 4, &tm_ptr,
+ &tm_out_len);
+ if (status != 0)
+ {
+ printf("Crypto_AOS_ProcessSecurity Failed with status = %d\n", status);
+ }
+ }
+ else
+ {
+ status = Crypto_TM_ProcessSecurity(tm_process_in + 4, (const uint16_t)tm_process_len - 4, &tm_ptr,
+ &tm_out_len);
+ if (status != 0)
+ {
+ printf("Crypto_TM_ProcessSecurity Failed with status = %d\n", status);
+ }
+ }
+#else
+ if (tm_debug == 1)
+ {
+ printf("Processing frame without ASM...\n");
+ }
+ status = Crypto_TM_ProcessSecurity(tm_process_in, (const uint16_t)tm_process_len, &tm_ptr, &tm_out_len);
+ if (status != 0)
+ {
+ printf("Crypto_TM_ProcessSecurity Failed with status = %d\n", status);
+ }
+#endif
+ if (status == CRYPTO_LIB_SUCCESS)
+ {
+ if (tm_debug == 1)
+ {
+ if ((tm_ptr[4] == 0x07) && (tm_ptr[5] == 0xFF))
+ {
+ // OID Frame
+ }
+ else
+ {
+ printf("crypto_standalone_tm_process: 1 - status = %d, decrypted[%d]: 0x", status, tm_out_len);
+ for (int i = 0; i < tm_out_len; i++)
+ {
+ printf("%02x", tm_ptr[i]);
+ }
+ printf("\n");
+ }
+ }
+
+/* Frame */
+#ifdef CRYPTO_STANDALONE_HANDLE_FRAMING
+#ifdef TM_CADU_HAS_ASM
+ uint16_t spi = (tm_process_in[10] << 8) | tm_process_in[11];
+ crypto_standalone_tm_frame(tm_ptr, tm_out_len, tm_framed, &tm_framed_len, spi);
+#else
+ uint16_t spi = (tm_process_in[6] << 8) | tm_process_in[7];
+ crypto_standalone_tm_frame(tm_process_in, tm_process_len, tm_framed, &tm_framed_len, spi);
+#endif
+ memcpy(tm_process_in, tm_framed, tm_framed_len);
+ tm_process_len = tm_framed_len;
+ tm_framed_len = 0;
+
+ if (tm_debug == 1)
+ // Note: Need logic to allow broken packet assembly
+ {
+ printf("crypto_standalone_tm_process: 2 - beginning after first header pointer - deframed[%d]: 0x",
+ tm_process_len);
+ for (int i = 0; i < tm_process_len; i++)
+ {
+ printf("%02x", tm_framed[i]);
+ }
+ printf("\n");
+ }
+#endif
+
+ /* Space Packet Protocol Loop */
+ tm_ptr = &tm_process_in[0];
+ while (tm_process_len > 5)
+ {
+ // SPP Telemetry OR SPP Idle Packet
+ crypto_standalone_spp_telem_or_idle(&status, tm_ptr, &spp_len, tm_socks, &tm_process_len);
+ tm_ptr = &tm_ptr[spp_len];
+ }
+ }
+ else
+ {
+ printf("crypto_standalone_tm_process - ProcessSecurity error %d \n", status);
+ }
+
+ /* Reset */
+ memset(tm_process_in, 0x00, sizeof(tm_process_in));
+ tm_process_len = 0;
+ memset(tm_ptr, 0x00, sizeof(tm_process_in));
+#ifdef CRYPTO_STANDALONE_TM_PROCESS_DEBUG
+ printf("\n");
+#endif
+ }
+
+ /* Delay */
+ usleep(10);
+ }
+ close(tm_read_sock->sockfd);
+ close(tm_write_sock->sockfd);
+ return tm_read_sock;
+}
+
+void crypto_standalone_cleanup(const int signal)
+{
+ if (signal == SIGINT)
+ {
+ printf("\n");
+ printf("Received CTRL+C, cleaning up... \n");
+ }
+ /* Signal threads to stop */
+ keepRunning = CRYPTO_LIB_ERROR;
+ exit(signal);
+ return;
+}
+
+int main(int argc, char *argv[])
+{
+ int32_t status = CRYPTO_LIB_SUCCESS;
+
+ // char input_buf[CRYPTO_MAX_INPUT_BUF];
+ // char input_tokens[CRYPTO_MAX_INPUT_TOKENS][CRYPTO_MAX_INPUT_TOKEN_SIZE];
+ // int num_input_tokens;
+ // int cmd;
+ // char *token_ptr;
+
+ udp_interface_t tc_apply;
+ udp_interface_t tm_process;
+
+ pthread_t tc_apply_thread;
+ pthread_t tm_process_thread;
+
+ tc_apply.read.ip_address = CRYPTOLIB_HOSTNAME;
+ tc_apply.read.port = TC_APPLY_PORT;
+ tc_apply.write.ip_address = SC_HOSTNAME;
+ tc_apply.write.port = TC_APPLY_FWD_PORT;
+ tm_process.read.ip_address = CRYPTOLIB_HOSTNAME;
+ tm_process.read.port = TM_PROCESS_PORT;
+ tm_process.write.ip_address = GSW_HOSTNAME;
+ tm_process.write.port = TM_PROCESS_FWD_PORT;
+
+ printf("Starting CryptoLib in standalone mode! \n");
+ if (argc != 1)
+ {
+ printf("Invalid number of arguments! \n");
+ printf(" Expected zero but received: %s \n", argv[1]);
+ }
+ printf("CryptoLib using %s sockets\n", crypto_use_tcp ? "TCP" : "UDP");
+
+ /* Catch CTRL+C */
+ signal(SIGINT, crypto_standalone_cleanup);
+
+ /* Startup delay */
+ sleep(10);
+
+ /* Initialize CryptoLib */
+ status = crypto_reset();
+ if (status != CRYPTO_LIB_SUCCESS)
+ {
+ printf("CryptoLib initialization failed with error %d \n", status);
+ keepRunning = CRYPTO_LIB_ERROR;
+ }
+
+ /* Initialize sockets */
+ if (keepRunning == CRYPTO_LIB_SUCCESS)
+ {
+ status = crypto_standalone_socket_init(&tc_apply.read, TC_APPLY_PORT, 0, 0); // udp 6010
+ if (status != CRYPTO_LIB_SUCCESS)
+ {
+ printf("crypto_standalone_socket_init tc_apply.read failed with status %d \n", status);
+ keepRunning = CRYPTO_LIB_ERROR;
+ }
+ else
+ {
+ status = crypto_standalone_socket_init(&tc_apply.write, TC_APPLY_FWD_PORT, 0,
+ crypto_use_tcp); // tcp, connect() 8010
+ if (status != CRYPTO_LIB_SUCCESS)
+ {
+ printf("crypto_standalone_socket_init tc_apply.write failed with status %d \n", status);
+ keepRunning = CRYPTO_LIB_ERROR;
+ }
+ }
+ }
+
+ if (keepRunning == CRYPTO_LIB_SUCCESS)
+ {
+ status =
+ crypto_standalone_socket_init(&tm_process.read, TM_PROCESS_PORT, 1, crypto_use_tcp); // tcp, accept() 8011
+ if (status != CRYPTO_LIB_SUCCESS)
+ {
+ printf("crypto_standalone_socket_init tm_apply.read failed with status %d \n", status);
+ keepRunning = CRYPTO_LIB_ERROR;
+ }
+ else
+ {
+ status = crypto_standalone_socket_init(&tm_process.write, TM_PROCESS_FWD_PORT, 0, 0); // udp 6011
+ if (status != CRYPTO_LIB_SUCCESS)
+ {
+ printf("crypto_standalone_socket_init tm_process.write failed with status %d \n", status);
+ keepRunning = CRYPTO_LIB_ERROR;
+ }
+ }
+ }
+
+ /* Start threads */
+ if (keepRunning == CRYPTO_LIB_SUCCESS)
+ {
+ printf(" TC Apply \n");
+ printf(" Read, UDP - %s : %d \n", tc_apply.read.ip_address, tc_apply.read.port);
+ printf(" Write, %s - %s : %d \n", crypto_use_tcp ? "TCP" : "UDP", tc_apply.write.ip_address,
+ tc_apply.write.port);
+ printf(" TM Process \n");
+ printf(" Read, %s - %s : %d \n", crypto_use_tcp ? "TCP" : "UDP", tm_process.read.ip_address,
+ tm_process.read.port);
+ printf(" Write, UDP - %s : %d \n", tm_process.write.ip_address, tm_process.write.port);
+ printf("\n");
+
+ status = pthread_create(&tc_apply_thread, NULL, *crypto_standalone_tc_apply, &tc_apply);
+ if (status < 0)
+ {
+ perror("Failed to create tc_apply_thread thread");
+ keepRunning = CRYPTO_LIB_ERROR;
+ }
+ else
+ {
+ status = pthread_create(&tm_process_thread, NULL, *crypto_standalone_tm_process, &tm_process);
+ if (status < 0)
+ {
+ perror("Failed to create tm_process_thread thread");
+ keepRunning = CRYPTO_LIB_ERROR;
+ }
+ }
+ }
+
+ /* Main loop */
+ while (keepRunning == CRYPTO_LIB_SUCCESS)
+ {
+ // num_input_tokens = -1;
+ // cmd = CRYPTO_CMD_UNKNOWN;
+
+ /* Read user input */
+/* commented this out because it fills up volume while the cryptolib container is running */
+/* printf(CRYPTO_PROMPT); */
+ // fgets(input_buf, CRYPTO_MAX_INPUT_BUF, stdin);
+
+ /* Tokenize line buffer */
+ // token_ptr = strtok(input_buf, " \t\n");
+ // while ((num_input_tokens < CRYPTO_MAX_INPUT_TOKENS) && (token_ptr != NULL))
+ // {
+ // if (num_input_tokens == -1)
+ // {
+ // /* First token is command */
+ // cmd = crypto_standalone_get_command(token_ptr);
+ // }
+ // else
+ // {
+ // strncpy(input_tokens[num_input_tokens], token_ptr, CRYPTO_MAX_INPUT_TOKEN_SIZE);
+ // }
+ // token_ptr = strtok(NULL, " \t\n");
+ // num_input_tokens++;
+ // }
+
+ /* Process command if valid */
+ // if (num_input_tokens >= 0)
+ // {
+ // crypto_standalone_process_command(cmd, num_input_tokens, &input_tokens[0][0]);
+ // }
+ }
+
+ /* Cleanup */
+ close(tc_apply.read.sockfd);
+ close(tc_apply.write.sockfd);
+ close(tm_process.read.sockfd);
+ close(tm_process.write.sockfd);
+
+ Crypto_Shutdown();
+
+ printf("\n");
+ exit(status);
+}
diff --git a/deployments/services/fsw/components/cryptolib/standalone/standalone.h b/deployments/services/fsw/components/cryptolib/standalone/standalone.h
new file mode 100755
index 000000000..c4857b5b3
--- /dev/null
+++ b/deployments/services/fsw/components/cryptolib/standalone/standalone.h
@@ -0,0 +1,138 @@
+/* Copyright (C) 2009 - 2022 National Aeronautics and Space Administration.
+ All Foreign Rights are Reserved to the U.S. Government.
+
+ This software is provided "as is" without any warranty of any kind, either expressed, implied, or statutory,
+ including, but not limited to, any warranty that the software will conform to specifications, any implied warranties
+ of merchantability, fitness for a particular purpose, and freedom from infringement, and any warranty that the
+ documentation will conform to the program, or any warranty that the software will be error free.
+
+ In no event shall NASA be liable for any damages, including, but not limited to direct, indirect, special or
+ consequential damages, arising out of, resulting from, or in any way connected with the software or its
+ documentation, whether or not based upon warranty, contract, tort or otherwise, and whether or not loss was sustained
+ from, or arose out of the results of, or use of, the software, documentation or services provided hereunder.
+
+ ITC Team
+ NASA IV&V
+ jstar-development-team@mail.nasa.gov
+*/
+
+#ifndef CRYPTOLIB_STANDALONE_H
+#define CRYPTOLIB_STANDALONE_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/*
+** Includes
+*/
+#include
+#include
+#include
+#include
+#include //hostent
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "crypto.h"
+#include "crypto_config.h"
+
+/*
+** Configuration
+*/
+#define CRYPTOLIB_HOSTNAME "cryptolib"
+#define GSW_HOSTNAME "cosmos"
+#define SC_HOSTNAME "radio-sim"
+
+#ifndef CRYPTO_RX_GROUND_PORT
+#define TC_APPLY_PORT 6010
+#endif
+#ifndef CRYPTO_RX_GROUND_PORT
+#define TC_APPLY_FWD_PORT 8010
+#endif
+#ifndef CRYPTO_RX_GROUND_PORT
+#define TM_PROCESS_PORT 8011
+#endif
+#ifndef CRYPTO_RX_GROUND_PORT
+#define TM_PROCESS_FWD_PORT 6011
+#endif
+
+#define CRYPTO_STANDALONE_HANDLE_FRAMING
+#define CRYPTO_STANDALONE_FRAMING_SCID 3
+#define CRYPTO_STANDALONE_FRAMING_VCID 0x00
+#define CRYPTO_STANDALONE_FRAMING_TC_DATA_LEN 512
+
+/*
+** Can be used to reduce ground system error messages
+*/
+#define CRYPTO_STANDALONE_DISCARD_IDLE_PACKETS
+#define CRYPTO_STANDALONE_DISCARD_IDLE_FRAMES
+
+/*
+** Defines
+*/
+#define CRYPTO_PROMPT "cryptolib> "
+#define CRYPTO_MAX_INPUT_BUF 512
+#define CRYPTO_MAX_INPUT_TOKENS 32
+#define CRYPTO_MAX_INPUT_TOKEN_SIZE 64
+
+#define TM_PRI_HDR_LENGTH 6
+#define TM_ASM_LENGTH 4
+#define SDLS_SPI_LENGTH 2
+
+#define CRYPTO_CMD_UNKNOWN (-1)
+#define CRYPTO_CMD_HELP 0
+#define CRYPTO_CMD_EXIT 1
+#define CRYPTO_CMD_NOOP 2
+#define CRYPTO_CMD_RESET 3
+#define CRYPTO_CMD_VCID 4
+#define CRYPTO_CMD_TC_DEBUG 5
+#define CRYPTO_CMD_TM_DEBUG 6
+#define CRYPTO_CMD_ACTIVE 7
+
+ /*
+ ** Structures
+ */
+ typedef struct
+ {
+ int sockfd;
+ char *ip_address;
+ int port;
+ struct sockaddr_in saddr;
+ } udp_info_t;
+
+ typedef struct
+ {
+ udp_info_t read;
+ udp_info_t write;
+ } udp_interface_t;
+
+ /*
+ ** Prototypes
+ */
+ int32_t crypto_standalone_check_number_arguments(int actual, int expected);
+ void crypto_standalone_to_lower(char *str);
+ void crypto_standalone_print_help(void);
+ int32_t crypto_standalone_get_command(const char *str);
+ int32_t crypto_standalone_process_command(int32_t cc, int32_t num_tokens, char *tokens);
+ int32_t crypto_host_to_ip(const char *hostname, char *ip);
+ int32_t crypto_standalone_udp_init(udp_info_t *sock, int32_t port, uint8_t bind_sock);
+ int32_t crypto_reset(void);
+ void crypto_standalone_tc_frame(uint8_t *in_data, uint16_t in_length, uint8_t *out_data, uint16_t *out_length);
+ void *crypto_standalone_tc_apply(void *socks);
+ void crypto_standalone_tm_frame(uint8_t *in_data, uint16_t in_length, uint8_t *out_data, uint16_t *out_length,
+ uint16_t spi);
+ void *crypto_standalone_tm_process(void *socks);
+ void crypto_standalone_cleanup(const int signal);
+
+#ifdef __cplusplus
+} /* Close scope of 'extern "C"' declaration which encloses file. */
+#endif
+
+#endif // CRYPTOLIB_STANDALONE_H
diff --git a/deployments/services/fsw/entrypoint.sh b/deployments/services/fsw/entrypoint.sh
new file mode 100755
index 000000000..01030a8f4
--- /dev/null
+++ b/deployments/services/fsw/entrypoint.sh
@@ -0,0 +1,34 @@
+#!/bin/bash
+
+# if [ -n "${HEALTHCHECK_PORT}" ]; then
+
+# if (( ${HEALTHCHECK_PORT} )); then # only if port is a number proceed
+# pkill -f nc ||
+# while true; do (echo -e 'HTTP/1.1 200 OK\r\n'; echo -e "\n\tSuccess: health check port ${HEALTHCHECK_PORT}" ; echo -e "\t$(date -u +%FT%T)\n") | nc -lp ${HEALTHCHECK_PORT}; done &
+# else
+# echo "HEALTHCHECK_PORT variable is set but to a none number; therefore, not starting health check action"
+# fi
+
+# else
+# echo "HEALTHCHECK_PORT variable is not set; therefore, not starting health check action. Optional. Proceeding"
+# fi
+
+pkill -f fsw_respawn.sh || true
+pkill -f core-cpu1 || true
+
+export LD_LIBRARY_PATH=${FSW_DIR}/cf/:${LD_LIBRARY_PATH}
+
+mkdir -p $FSW_DIR/data
+mkdir -p $FSW_DIR/data/cam
+mkdir -p $FSW_DIR/data/evs
+mkdir -p $FSW_DIR/data/hk
+mkdir -p $FSW_DIR/data/inst
+touch $FSW_DIR/data/dummy.txt
+echo "1234567890" > $FSW_DIR/data/dummy.txt
+truncate -s 1M $FSW_DIR/data/dummy.txt
+
+#
+cd ${FSW_DIR} && \
+ ./core-cpu1 -R PO 2>&1 | tee -a ${BASE_DIR}/core-cpu1.log &
+
+tail -f /dev/null
diff --git a/deployments/services/fsw/libuart.c b/deployments/services/fsw/libuart.c
new file mode 100755
index 000000000..495478abb
--- /dev/null
+++ b/deployments/services/fsw/libuart.c
@@ -0,0 +1,230 @@
+/* Copyright (C) 2009 - 2018 National Aeronautics and Space Administration. All Foreign Rights are Reserved to the U.S. Government.
+
+This software is provided "as is" without any warranty of any, kind either express, implied, or statutory, including, but not
+limited to, any warranty that the software will conform to, specifications any implied warranties of merchantability, fitness
+for a particular purpose, and freedom from infringement, and any warranty that the documentation will conform to the program, or
+any warranty that the software will be error free.
+
+In no event shall NASA be liable for any damages, including, but not limited to direct, indirect, special or consequential damages,
+arising out of, resulting from, or in any way connected with the software or its documentation. Whether or not based upon warranty,
+contract, tort or otherwise, and whether or not loss was sustained from, or arose out of the results of, or use of, the software,
+documentation or services provided hereunder
+
+ITC Team
+NASA IV&V
+ivv-itc@lists.nasa.gov
+*/
+
+#include "libuart.h"
+
+int32_t uart_init_port(uart_info_t* device)
+{
+ int32_t status = UART_SUCCESS;
+ speed_t speed;
+
+ // Set the access flag. We default to O_RDWR if the specified access flag is
+ // out of range
+ int oflag = O_RDWR;
+
+ if (device->access_option == uart_access_flag_RDONLY)
+ oflag = O_RDONLY;
+ else if (device->access_option == uart_access_flag_WRONLY)
+ oflag = O_WRONLY;
+
+ device->handle = open(device->deviceString, oflag);
+
+ if (device->handle >= 0)
+ {
+ // Set open flag
+ device->isOpen = PORT_OPEN;
+
+ // Get current port options
+ if(tcgetattr(device->handle, &device->options)<0)
+ {
+ status = OS_ERR_FILE;
+ fprintf(stderr, "***************** ERROR: Something went wrong: 1\n");
+ return status;
+ }
+
+ // Set baud rate
+ switch(device->baud)
+ {
+ case 4800:
+ speed=B4800;
+ break;
+ case 9600:
+ speed=B9600;
+ break;
+ case 19200:
+ speed=B19200;
+ break;
+ case 38400:
+ speed=B38400;
+ break;
+ case 57600:
+ speed=B57600;
+ break;
+ case 115200:
+ speed=B115200;
+ break;
+ case 230400:
+ speed=B230400;
+ break;
+ case 460800:
+ speed=B460800;
+ break;
+ case 500000:
+ speed=B500000;
+ break;
+ case 576000:
+ speed=B576000;
+ break;
+ case 921600:
+ speed=B921600;
+ break;
+ case 1000000:
+ speed=B1000000;
+ break;
+ case 1152000:
+ speed=B1152000;
+ break;
+ case 2000000:
+ speed=B2000000;
+ break;
+ case 2500000:
+ speed=B2500000;
+ break;
+ case 3000000:
+ speed=B3000000;
+ break;
+ case 3500000:
+ speed=B3500000;
+ break;
+ case 4000000:
+ speed=B4000000;
+ break;
+ default:
+ status = OS_ERR_FILE;
+ fprintf(stderr, "***************** ERROR: Something went wrong: 2\n");
+ return status;
+ }
+ if(cfsetispeed(&device->options,speed)<0)
+ {
+ status = OS_ERR_FILE;
+ fprintf(stderr, "***************** ERROR: Something went wrong: 3\n");
+ return status;
+ }
+ if(cfsetospeed(&device->options,speed)<0)
+ {
+ status = OS_ERR_FILE;
+ fprintf(stderr, "***************** ERROR: Something went wrong: 4\n");
+ return status;
+ }
+
+ // Raw byte mode - no strings and no CRLF
+ device->options.c_iflag = IGNBRK | INPCK;
+ device->options.c_oflag = 0;
+ device->options.c_cflag = CREAD | CS8 | CLOCAL;
+ device->options.c_lflag = NOFLSH;
+ if(device->canonicalModeOn == 1)
+ {
+ device->options.c_lflag |= ICANON;
+ }
+
+ // Set the port to blocking read with timeout of 0.1 sec
+ device->options.c_cc[VMIN] = 0; // min of bytes to read
+ device->options.c_cc[VTIME] = 1; // intra-byte time to wait - tenths of sec
+ fcntl(device->handle, F_SETFL, O_NONBLOCK); // Don't have serial port block
+
+ // TODO - any other options needed like hw control?
+ tcflush(device->handle, TCIOFLUSH);
+
+ // Set the options
+ if(tcsetattr(device->handle, TCSANOW, &device->options)<0)
+ {
+ status = OS_ERR_FILE;
+ fprintf(stderr, "***************** ERROR: Something went wrong: 5\n");
+ return status;
+ }
+ }
+ else
+ {
+ printf("Oh no! Open \"%s\" failed and reported: %s \n", device->deviceString, strerror(device->handle));
+ device->isOpen = PORT_CLOSED;
+ status = OS_ERR_FILE;
+ fprintf(stderr, "***************** ERROR: Something went wrong: 6\n");
+
+ }
+
+ return status;
+}
+
+int32_t uart_bytes_available(uart_info_t* device)
+{
+ int32_t bytes_available = 0;
+
+ ioctl(device->handle, FIONREAD, &bytes_available);
+
+ return bytes_available;
+}
+
+int32_t uart_flush(uart_info_t* device)
+{
+ tcflush(device->handle,TCIOFLUSH);
+
+ return UART_SUCCESS;
+}
+
+int32_t uart_read_port(uart_info_t* device, uint8_t data[], const uint32_t numBytes)
+{
+ int32_t status = UART_SUCCESS;
+
+ if (data != NULL)
+ {
+ // TODO - this read blocks forever if no serial data on the port.
+ // it should be timing out - need to look into this ASAP
+ status = read(device->handle, data, numBytes);
+ }
+ else
+ {
+ status = OS_ERR_FILE;
+ fprintf(stderr, "***************** ERROR: Something went wrong: 7\n");
+
+ }
+
+ return status;
+}
+
+int32_t uart_write_port(uart_info_t* device, uint8_t data[], const uint32_t numBytes)
+{
+ int32_t status = UART_SUCCESS;
+
+ status = write(device->handle, data, numBytes);
+
+ return status;
+}
+
+int32_t uart_close_port(uart_info_t* device)
+{
+ int32_t status = UART_SUCCESS;
+
+ if (device->handle >= 0)
+ {
+ status = close(device->handle);
+ if (0 == status) /* todo remove magic number */
+ {
+ status = UART_SUCCESS;
+ }
+ else
+ {
+ status = OS_ERR_FILE;
+ fprintf(stderr, "***************** ERROR: Something went wrong: 8\n");
+ }
+ }
+ else
+ {
+ status = OS_ERR_FILE;
+ fprintf(stderr, "***************** ERROR: Something went wrong: 9\n");
+ }
+ return status;
+}
diff --git a/deployments/services/fsw/scripts/cfg/prepare.sh b/deployments/services/fsw/scripts/cfg/prepare.sh
new file mode 100755
index 000000000..26a87143c
--- /dev/null
+++ b/deployments/services/fsw/scripts/cfg/prepare.sh
@@ -0,0 +1,16 @@
+#!/bin/bash -i
+#
+# Convenience script for NOS3 development
+#
+SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
+source $SCRIPT_DIR/env.sh
+echo ""
+
+echo "Create local user directory... ${USER_NOS3_DIR}"
+mkdir -p $USER_NOS3_DIR
+mkdir -p $USER_NOS3_DIR/42
+echo ""
+
+echo "Preparing Shared Folders for Fprime... ${$USER_FPRIME_PATH}"
+mkdir -p $USER_FPRIME_PATH
+echo ""
\ No newline at end of file
diff --git a/deployments/services/openmct/Containerfile b/deployments/services/openmct/Containerfile
new file mode 100644
index 000000000..a2fe91136
--- /dev/null
+++ b/deployments/services/openmct/Containerfile
@@ -0,0 +1,65 @@
+ARG IMAGE_URI=docker.io/library/ubuntu:25.04
+
+FROM ${IMAGE_URI}
+
+#--- PROXY CONFIG (if needed)
+ARG HTTPS_PROXY
+ARG HTTP_PROXY
+ARG NO_PROXY
+ARG DEPLOYMENT_ENVIRO
+
+ENV HTTPS_PROXY=${HTTPS_PROXY}
+ENV HTTP_PROXY=${HTTP_PROXY}
+ENV NO_PROXY=${NO_PROXY}
+ENV DEPLOYMENT_ENVIRO=${DEPLOYMENT_ENVIRO}
+
+# Install dependencies
+RUN apt-get update && \
+ apt-get -y install \
+ sudo git curl && \
+ apt-get clean && \
+ rm -rf /var/lib/apt/lists/*
+
+# Set working directory
+WORKDIR /opt
+
+# Install nvm and node
+ARG NVM_VERSION
+ARG NODE_VERSION
+
+ENV NVM_VERSION=${NVM_VERSION}
+ENV NVM_URL=https://raw.githubusercontent.com/nvm-sh/nvm/${NVM_VERSION}/install.sh
+ENV NVM_DIR=/opt/nvm
+RUN mkdir -p $NVM_DIR
+
+ENV NODE_VERSION=${NODE_VERSION}
+ENV NODE_PATH=$NVM_DIR/versions/node/${NODE_VERSION}/bin
+ENV PATH=${NVM_DIR}:${NODE_PATH}:${PATH}
+
+# Install nvm and node
+WORKDIR /opt
+RUN curl -o- ${NVM_URL} | bash
+RUN . ${NVM_DIR}/nvm.sh && nvm install ${NODE_VERSION}
+
+# Clone openmct-yamcs repo
+ARG GIT_URL
+ARG GIT_COMMIT
+
+ENV GIT_URL=${GIT_URL}
+ENV GIT_COMMIT=${GIT_COMMIT}
+
+RUN git clone --recurse-submodules -b ${GIT_COMMIT} -j2 ${GIT_URL} openmct
+
+# Set working directory to the cloned repo
+WORKDIR /opt/openmct
+
+# Install dependencies and build openmct-yamcs
+RUN ${NODE_PATH}/npm install -loglevel verbose
+RUN ${NODE_PATH}/npm run build:example:master -loglevel verbose
+
+# Install dependencies
+RUN apt-get update && \
+ apt-get -y install \
+ sudo iputils-ping vim netcat-traditional tmux tree && \
+ apt-get clean && \
+ rm -rf /var/lib/apt/lists/*
diff --git a/deployments/services/openmct/entrypoint.sh b/deployments/services/openmct/entrypoint.sh
new file mode 100755
index 000000000..ba0d12884
--- /dev/null
+++ b/deployments/services/openmct/entrypoint.sh
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+
+pkill -f node
+pkill -f npm
+
+npm start
+
+tail -f /dev/null
diff --git a/deployments/services/openmct/example/index.js b/deployments/services/openmct/example/index.js
new file mode 100644
index 000000000..257bfa154
--- /dev/null
+++ b/deployments/services/openmct/example/index.js
@@ -0,0 +1,229 @@
+import installYamcsPlugins from '../src/openmct-yamcs.js';
+
+const config = {
+ yamcsDictionaryEndpoint: "http://localhost:9000/yamcs-proxy/",
+ yamcsHistoricalEndpoint: "http://localhost:9000/yamcs-proxy/",
+ yamcsWebsocketEndpoint: "ws://localhost:9000/yamcs-proxy-ws/",
+ yamcsUserEndpoint: "http://localhost:9000/yamcs-proxy/api/user/",
+ yamcsInstance: "nos3",
+ yamcsProcessor: "realtime",
+ yamcsFolder: "nos3",
+ throttleRate: 1000,
+ // Batch size is specified in characers as there is no performant way of calculating true
+ // memory usage of a string buffer in real-time.
+ // String characters can be 8 or 16 bits in JavaScript, depending on the code page used.
+ // Thus 500,000 characters requires up to 16MB of memory (1,000,000 * 16).
+ maxBufferSize: 1000000
+};
+const STATUS_STYLES = {
+ NO_STATUS: {
+ iconClass: "icon-question-mark",
+ iconClassPoll: "icon-status-poll-question-mark"
+ },
+ GO: {
+ iconClass: "icon-check",
+ iconClassPoll: "icon-status-poll-question-mark",
+ statusClass: "s-status-ok",
+ statusBgColor: "#33cc33",
+ statusFgColor: "#000"
+ },
+ MAYBE: {
+ iconClass: "icon-alert-triangle",
+ iconClassPoll: "icon-status-poll-question-mark",
+ statusClass: "s-status-warning",
+ statusBgColor: "#ffb66c",
+ statusFgColor: "#000"
+ },
+ NO_GO: {
+ iconClass: "icon-circle-slash",
+ iconClassPoll: "icon-status-poll-question-mark",
+ statusClass: "s-status-error",
+ statusBgColor: "#9900cc",
+ statusFgColor: "#fff"
+ }
+};
+const openmct = window.openmct;
+
+(() => {
+ const POLL_INTERVAL = 100; // ms
+ const MAX_POLL_TIME = 10000; // 10 seconds
+ const COMPOSITION_RETRY_DELAY = 250; // ms
+ const MAX_COMPOSITION_RETRIES = 20; // 5 seconds total with 250ms intervals
+ const ONE_SECOND = 1000;
+ const ONE_MINUTE = ONE_SECOND * 60;
+ const THIRTY_MINUTES = ONE_MINUTE * 30;
+
+ openmct.setAssetPath("/node_modules/openmct/dist");
+
+ installDefaultPlugins();
+ openmct.install(installYamcsPlugins(config));
+ openmct.install(
+ openmct.plugins.OperatorStatus({ statusStyles: STATUS_STYLES })
+ );
+
+ document.addEventListener("DOMContentLoaded", function () {
+ openmct.start();
+ });
+ openmct.install(
+ openmct.plugins.Conductor({
+ menuOptions: [
+ {
+ name: "Realtime",
+ timeSystem: "utc",
+ clock: "local",
+ clockOffsets: {
+ start: -THIRTY_MINUTES,
+ end: 0
+ }
+ },
+ {
+ name: "Fixed",
+ timeSystem: "utc",
+ bounds: {
+ start: Date.now() - THIRTY_MINUTES,
+ end: 0
+ }
+ }
+ ]
+ })
+ );
+
+ function installDefaultPlugins() {
+ openmct.install(openmct.plugins.LocalStorage());
+ openmct.install(openmct.plugins.Espresso());
+ openmct.install(openmct.plugins.MyItems());
+ openmct.install(openmct.plugins.example.Generator());
+ openmct.install(openmct.plugins.example.ExampleImagery());
+ openmct.install(openmct.plugins.UTCTimeSystem());
+ openmct.install(openmct.plugins.TelemetryMean());
+
+ openmct.install(
+ openmct.plugins.DisplayLayout({
+ showAsView: ["summary-widget", "example.imagery", "yamcs.image"]
+ })
+ );
+ openmct.install(openmct.plugins.SummaryWidget());
+ openmct.install(openmct.plugins.Notebook());
+ openmct.install(openmct.plugins.LADTable());
+ openmct.install(
+ openmct.plugins.ClearData([
+ "table",
+ "telemetry.plot.overlay",
+ "telemetry.plot.stacked"
+ ])
+ );
+
+ openmct.install(openmct.plugins.FaultManagement());
+ openmct.install(openmct.plugins.BarChart());
+ openmct.install(openmct.plugins.Timeline());
+ openmct.install(openmct.plugins.EventTimestripPlugin());
+
+ // setup example display layout
+ openmct.on('start', async () => {
+ if (localStorage.getItem('exampleLayout') !== null) {
+ return;
+ }
+
+ // try to import the example display layout, fail gracefully
+ try {
+ // Function to fetch JSON content as text
+ async function fetchJsonText(url) {
+ const response = await fetch(url);
+ const text = await response.text();
+
+ return text;
+ }
+
+ async function getExampleLayoutPath() {
+ const objects = Object.values(JSON.parse(localStorage.getItem('mct')));
+ const exampleLayout = objects.find(object => object.name === 'Example Flexible Layout');
+ let path = await openmct.objects.getOriginalPath(exampleLayout.identifier);
+
+ path.pop();
+ path = path.reverse();
+ path = path.reduce((prev, curr) => {
+ return prev + '/' + openmct.objects.makeKeyString(curr.identifier);
+ }, '#/browse');
+
+ return path;
+ }
+
+ // poll for the localStorage item
+ function mctItemExists() {
+ return new Promise((resolve, reject) => {
+ const startTime = Date.now();
+
+ function checkItem() {
+ if (localStorage.getItem('mct') !== null) {
+ resolve(true);
+
+ return;
+ }
+
+ if (Date.now() - startTime > MAX_POLL_TIME) {
+ reject(new Error('Timeout waiting for mct localStorage item'));
+
+ return;
+ }
+
+ setTimeout(checkItem, POLL_INTERVAL);
+ }
+
+ checkItem();
+ });
+ }
+
+ // wait for the 'mct' item to exist
+ await mctItemExists();
+
+ // setup to use import as JSON action
+ const importAction = openmct.actions.getAction('import.JSON');
+ const myItems = await openmct.objects.get('mine');
+ const exampleDisplayText = await fetchJsonText('./example-display.json');
+ const selectFile = {
+ body: exampleDisplayText
+ };
+
+ // import the example display layout
+ importAction.onSave(myItems, { selectFile });
+
+ // the importAction has asynchronous code, so we will need to check
+ // the composition of My Items to confirm the import was successful
+ const compositionCollection = openmct.composition.get(myItems);
+ let compositionLength = 0;
+ let composition;
+
+ let retryCount = 0;
+ while (compositionLength === 0 && retryCount < MAX_COMPOSITION_RETRIES) {
+ composition = await compositionCollection.load();
+ compositionLength = composition.length;
+
+ if (compositionLength === 0) {
+ retryCount++;
+ await new Promise(resolve => setTimeout(resolve, COMPOSITION_RETRY_DELAY));
+ }
+ }
+
+ if (compositionLength === 0) {
+ throw new Error('Failed to load composition after maximum retries');
+ }
+
+ const exampleLayoutPath = await getExampleLayoutPath();
+
+ // give everything time to initialize
+ await new Promise(resolve => setTimeout(resolve, 250));
+
+ openmct.notifications.info('Navigated to Example Display Layout');
+
+ // navigate to the "Example Display Layout"
+ openmct.router.navigate(exampleLayoutPath);
+
+ // set the localStorage item to prevent this from running again
+ localStorage.setItem('exampleLayout', 'true');
+ } catch (error) {
+ console.error('Failed to set up example display layout:', error);
+ openmct.notifications.error('Failed to load example display layout: ' + error.message);
+ }
+ });
+ }
+})();
diff --git a/deployments/services/openmct/example/make-example-events.mjs b/deployments/services/openmct/example/make-example-events.mjs
new file mode 100644
index 000000000..345a93c9f
--- /dev/null
+++ b/deployments/services/openmct/example/make-example-events.mjs
@@ -0,0 +1,127 @@
+import process from 'process';
+
+const INSTANCE = "nos3";
+const URL = `http://localhost:8090/api/archive/${INSTANCE}/events`;
+
+const events = [
+ {
+ type: "PRESSURE_ALERT",
+ message: "Pressure threshold exceeded",
+ severity: "CRITICAL",
+ source: "PressureModule",
+ sequenceNumber: 1,
+ extra: {
+ pressure: "150 PSI",
+ location: "Hydraulic System"
+ }
+ },
+ {
+ type: "PRESSURE_WARNING",
+ message: "Pressure nearing critical level",
+ severity: "WARNING",
+ source: "PressureModule",
+ sequenceNumber: 2,
+ extra: {
+ pressure: "140 PSI",
+ location: "Hydraulic System"
+ }
+ },
+ {
+ type: "PRESSURE_INFO",
+ message: "Pressure system check completed",
+ severity: "INFO",
+ source: "PressureModule",
+ sequenceNumber: 3,
+ extra: {
+ checkType: "Routine Inspection",
+ duration: "10m"
+ }
+ },
+ {
+ type: "TEMPERATURE_ALERT",
+ message: "Temperature threshold exceeded",
+ severity: "CRITICAL",
+ source: "TemperatureModule",
+ sequenceNumber: 4,
+ extra: {
+ temperature: "100°C",
+ location: "Engine Room"
+ }
+ },
+ {
+ type: "TEMPERATURE_WARNING",
+ message: "Temperature nearing critical level",
+ severity: "WARNING",
+ source: "TemperatureModule",
+ sequenceNumber: 5,
+ extra: {
+ temperature: "95°C",
+ location: "Engine Room"
+ }
+ },
+ {
+ type: "TEMPERATURE_INFO",
+ message: "Temperature nominal",
+ severity: "INFO",
+ source: "TemperatureModule",
+ sequenceNumber: 6,
+ extra: {
+ temperature: "35°C",
+ location: "Life Support"
+ }
+ },
+ {
+ type: "TEMPERATURE_INFO",
+ message: "Temperature nominal",
+ severity: "INFO",
+ source: "TemperatureModule",
+ sequenceNumber: 7,
+ extra: {
+ temperature: "30°C",
+ location: "Life Support"
+ }
+ },
+ {
+ type: "TEMPERATURE_SEVERE",
+ message: "Temperature nominal",
+ severity: "SEVERE",
+ source: "TemperatureModule",
+ sequenceNumber: 8,
+ extra: {
+ temperature: "200°C",
+ location: "Engine Room"
+ }
+ }
+];
+
+async function postEvent(event, delaySeconds) {
+ const eventTime = new Date(Date.now() + delaySeconds * 1000).toISOString();
+ event.time = eventTime;
+
+ try {
+ const response = await fetch(URL, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(event)
+ });
+
+ if (response.ok) {
+ console.log(`Event posted successfully: ${event.type}`);
+ } else {
+ console.error(`Failed to post event: ${event.type}. HTTP Status: ${response.status}`);
+ }
+ } catch (error) {
+ console.error(`Error posting event: ${event.type}.`, error);
+ }
+}
+
+export async function postAllEvents() {
+ for (let i = 0; i < events.length; i++) {
+ await postEvent(events[i], i * 5);
+ }
+}
+
+// If you still want to run it standalone
+if (import.meta.url === `file://${process.argv[1]}`) {
+ postAllEvents();
+}
diff --git a/deployments/services/openmct/webpack.dev.mjs b/deployments/services/openmct/webpack.dev.mjs
new file mode 100644
index 000000000..2d21c4e55
--- /dev/null
+++ b/deployments/services/openmct/webpack.dev.mjs
@@ -0,0 +1,71 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2020, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT is 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.
+ *
+ * Open MCT includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+// @ts-check
+import path from "path";
+import { fileURLToPath } from "url";
+import { merge } from "webpack-merge";
+import commonConfig from "./webpack.common.mjs";
+
+// Replicate __dirname functionality for ES modules
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+
+/** @type {import('webpack').Configuration} */
+const devConfig = {
+ mode: "development",
+ context: path.resolve(__dirname, "../"),
+ devtool: "eval-source-map",
+ entry: {
+ "openmct-yamcs-example": path.resolve(__dirname, "../example/index.js"),
+ },
+ devServer: {
+ compress: true,
+ port: 9000,
+ static: [
+ {
+ directory: path.join(__dirname, "../example"),
+ },
+ {
+ directory: path.join(__dirname, "../node_modules/openmct/dist"),
+ publicPath: "/node_modules/openmct/dist",
+ },
+ ],
+ proxy: [
+ {
+ context: ["/yamcs-proxy/"],
+ target: "http://active-gs:8090/",
+ secure: false,
+ changeOrigin: true,
+ pathRewrite: { "^/yamcs-proxy/": "" },
+ },
+ {
+ context: ["/yamcs-proxy-ws/"],
+ target: "ws://active-gs:8090/api/websocket",
+ secure: false,
+ changeOrigin: true,
+ ws: true,
+ pathRewrite: { "^/yamcs-proxy-ws/": "" },
+ },
+ ],
+ },
+};
+
+export default merge(commonConfig, devConfig);
diff --git a/deployments/services/radio-sim/entrypoint.sh b/deployments/services/radio-sim/entrypoint.sh
new file mode 100755
index 000000000..4b269fce4
--- /dev/null
+++ b/deployments/services/radio-sim/entrypoint.sh
@@ -0,0 +1,7 @@
+#!/bin/bash -x
+
+cd /home/nos3/builds/nos3/sims/build/bin
+pkill -f ./nos3-single-simulator
+./nos3-single-simulator -f ./nos3-simulator.xml generic-radio-sim &
+
+tail -f /dev/null
diff --git a/deployments/services/radio-sim/entrypoint2.sh b/deployments/services/radio-sim/entrypoint2.sh
new file mode 100755
index 000000000..5b653d2e7
--- /dev/null
+++ b/deployments/services/radio-sim/entrypoint2.sh
@@ -0,0 +1,13 @@
+#!/bin/bash -x
+
+cd /home/nos3/builds/nos3/sims/build/bin
+pkill -f ./nos3-single-simulator
+./nos3-single-simulator -f ./nos3-simulator.xml generic-radio-sim &
+
+cd /home/nos3/builds/nos3/gsw/build
+pkill -f ./support/standalone
+./support/standalone &
+
+cd -
+
+tail -f /dev/null
diff --git a/deployments/services/yamcs/Dockerfile b/deployments/services/yamcs/Dockerfile
new file mode 100644
index 000000000..0817bd34d
--- /dev/null
+++ b/deployments/services/yamcs/Dockerfile
@@ -0,0 +1,87 @@
+ARG REGISTRY_HOST=ghcr.io
+ARG IMAGE_USERNAME=haisamido
+ARG IMAGE_NAME=nos3-base
+ARG IMAGE_TAG=dev
+
+ARG IMAGE_URI=${REGISTRY_HOST}/${IMAGE_USERNAME}/${IMAGE_NAME}:${IMAGE_TAG}
+
+#------------------------------------------------------------------------------
+ARG GIT_URL=https://github.com/nasa-itc/yamcs-nos3
+ARG GIT_COMMIT=dev
+
+#------------------------------------------------------------------------------
+ARG NOS3_USER=nos3
+ARG FLIGHT_SOFTWARE=cfs
+ARG GSW_SOFTWARE=yamcs
+
+################################################################################
+FROM ${IMAGE_URI} AS nos3-yamcs
+################################################################################
+USER root
+
+ARG DEBIAN_FRONTEND=noninteractive
+
+#------------------------------------------------------------------------------
+# Proxy configs
+#------------------------------------------------------------------------------
+ARG MAVEN_HTTPS_PROXY
+ARG MAVEN_REPO_LOCAL
+ARG COMPONENT_DIR
+
+ARG HTTPS_PROXY
+ARG HTTP_PROXY
+ARG NO_PROXY
+ARG DEPLOYMENT_ENVIRO
+
+# =/home/${NOS3_USER}/.m2/repository
+ENV MAVEN_HTTPS_PROXY=${MAVEN_HTTPS_PROXY}
+ENV MAVEN_REPO_LOCAL=${MAVEN_REPO_LOCAL}
+ENV COMPONENT_DIR=${COMPONENT_DIR}
+
+ENV HTTPS_PROXY=${HTTPS_PROXY}
+ENV HTTP_PROXY=${HTTP_PROXY}
+ENV NO_PROXY=${NO_PROXY}
+ENV DEPLOYMENT_ENVIRO=${DEPLOYMENT_ENVIRO}
+
+#------------------------------------------------------------------------------
+# Git Configs
+#------------------------------------------------------------------------------
+ARG GIT_URL
+ARG GIT_COMMIT
+
+ENV GIT_URL=${GIT_URL}
+ENV GIT_COMMIT=${GIT_COMMIT}
+
+#------------------------------------------------------------------------------
+# NOS3 Configs
+#------------------------------------------------------------------------------
+ARG FLIGHT_SOFTWARE
+ARG GSW_SOFTWARE
+ARG NOS3_USER
+ARG YAMCS_PORT
+
+ENV NOS3_USER=${NOS3_USER}
+ENV FLIGHT_SOFTWARE=${FLIGHT_SOFTWARE}
+ENV GSW_SOFTWARE=${GSW_SOFTWARE}
+ENV YAMCS_PORT=${YAMCS_PORT}
+#------------------------------------------------------------------------------
+
+ # Switch to the newly created user
+USER ${NOS3_USER}
+
+#------------------------------------------------------------------------------
+# Install the GSW Software
+#------------------------------------------------------------------------------
+WORKDIR /home/${NOS3_USER}/.nos3
+RUN rm -rf /home/${NOS3_USER}/.nos3/${GSW_SOFTWARE}
+RUN git clone --recurse-submodules -b ${GIT_COMMIT} -j4 ${GIT_URL} ${GSW_SOFTWARE} && \
+ cd ./${GSW_SOFTWARE} && \
+ git fetch && \
+ git pull origin
+
+WORKDIR /home/${NOS3_USER}/.nos3/${GSW_SOFTWARE}
+COPY settings.xml ./settings.xml
+
+RUN mvn -X ${MAVEN_HTTPS_PROXY} -DskipTests -Dmaven.repo.local=${MAVEN_REPO_LOCAL} -DCOMPONENT_DIR=${COMPONENT_DIR} install
+
+#------------------------------------------------------------------------------
diff --git a/deployments/services/yamcs/display.par b/deployments/services/yamcs/display.par
new file mode 100644
index 000000000..db878b48f
--- /dev/null
+++ b/deployments/services/yamcs/display.par
@@ -0,0 +1,25 @@
+{
+ "$schema": "https://yamcs.org/schema/parameter-table.schema.json",
+ "scroll": false,
+ "bufferSize": 18,
+ "parameters": [
+ "/CFS/CFE_EVS_PACKET/MESSAGE",
+ "/CFS/CFE_EVS_TLMPKT/MESSAGESENDCOUNTER",
+ "/CFS/CFE_TBL_HKPACKET/LASTUPDATETIME_SUBSECONDS",
+ "/CFS/DS_HKPACKET/FILEWRITECOUNTER",
+ "/NOVATEL_OEM615/NOVATEL_OEM615_DATA_TLM/GPS_FRAC_SECS",
+ "/SIM_42_TRUTH/SIM_42_TRUTH_DATA/BVB_1",
+ "/GENERIC_TORQUER/GENERIC_TORQUER_HK_TLM_T/TORQUER_DIRECTION_0",
+ "/GENERIC_EPS/GENERIC_EPS_HK_TLM/RAW_BATTERY_VOLTAGE",
+ "/GENERIC_ADCS/GENERIC_ADCS_DI/FSS_SVB_X",
+ "/GENERIC_ADCS/GENERIC_ADCS_DI/IMU_ACC_X",
+ "/GENERIC_MAG/GENERIC_MAG_DATA_TLM/RAW_MAG_X",
+ "/GENERIC_REACTION_WHEEL/GENRW_HK_TLM_T/MOMENTUM_NMS_0",
+ "/GENERIC_REACTION_WHEEL/GENRW_HK_TLM_T/MOMENTUM_NMS_1",
+ "/GENERIC_REACTION_WHEEL/GENRW_HK_TLM_T/MOMENTUM_NMS_2",
+ "/GENERIC_RADIO/GENERIC_RADIO_HK_TLM/FORWARD_COUNT",
+ "/yamcs/cosmos/links/radio-in/dataInCount",
+ "/yamcs/cosmos/links/radio-out/dataInCount",
+ "/SAMPLE/SAMPLE_HK_TLM/DEVICE_ENABLED"
+ ]
+}
diff --git a/deployments/services/yamcs/entrypoint.sh b/deployments/services/yamcs/entrypoint.sh
new file mode 100755
index 000000000..ea5aefd4b
--- /dev/null
+++ b/deployments/services/yamcs/entrypoint.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+pkill -9 java
+rm -rf ${YAMCS_DATA_DIR}/_global.rdb/LOCK
+rm -rf ${YAMCS_DATA_DIR}/nos3.rdb/LOCK
+
+mkdir -p /tmp/nos3
+mkdir -p /tmp/nos3/data
+mkdir -p /tmp/nos3/data/cam
+mkdir -p /tmp/nos3/data/evs
+mkdir -p /tmp/nos3/data/hk
+mkdir -p /tmp/nos3/data/inst
+mkdir -p /tmp/nos3/uplink
+
+# Start YAMCS server
+mvn -X ${MAVEN_HTTPS_PROXY} -Dmaven.repo.local=${MAVEN_REPO_LOCAL} -DCOMPONENT_DIR=${COMPONENT_DIR} yamcs:run
+
+tail -f /dev/null
diff --git a/deployments/targets/docker/entrypoint_nos_engine_server.sh b/deployments/targets/docker/entrypoint_nos_engine_server.sh
new file mode 100755
index 000000000..c28a9182e
--- /dev/null
+++ b/deployments/targets/docker/entrypoint_nos_engine_server.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+pkill -f nos_engine_server_standalone || true
+
+/usr/bin/nos_engine_server_standalone /builds/nos3/sims/build/bin/nos_engine_server_config.json
+
+tail -f /dev/null
diff --git a/deployments/targets/docker/hw-components/entrypoint.sh b/deployments/targets/docker/hw-components/entrypoint.sh
new file mode 100755
index 000000000..547c35fa0
--- /dev/null
+++ b/deployments/targets/docker/hw-components/entrypoint.sh
@@ -0,0 +1,82 @@
+#!/bin/bash
+
+DIR=/home/nos3
+
+pkill -f ./nos3-single-simulator
+pkill -f ./support/standalone
+
+# Radio Sim and Cryptolib
+cd /home/nos3/builds/nos3/sims/build/bin && \
+ ./nos3-single-simulator -f ./nos3-simulator.xml generic-radio-sim 2>&1 \
+ | tee -a ${DIR}/radio-sim.log | tee -a ${DIR}/hw-components.log &
+
+cd /home/nos3/builds/nos3/gsw/build && \
+ ./support/standalone 2>&1 \
+ | tee -a ${DIR}/cryptolib.log | tee -a ${DIR}/hw-components.log &
+
+# HW Components
+cd /home/nos3/builds/nos3/fsw/build/exe/cpu1 && \
+ /home/nos3/builds/nos3/scripts/fsw/onair_launch.sh 2>&1 \
+ | tee -a ${DIR}/onair_launch.log | tee -a ${DIR}/hw-components.log &
+
+cd /home/nos3/builds/nos3/sims/build/bin && \
+ ./nos3-single-simulator -f ./nos3-simulator.xml camsim 2>&1 \
+ | tee -a ${DIR}/camsim.log | tee -a ${DIR}/hw-components.log &
+
+cd /home/nos3/builds/nos3/sims/build/bin && \
+ ./nos3-single-simulator -f ./nos3-simulator.xml generic-css-sim 2>&1 \
+ | tee -a ${DIR}/css.log | tee -a ${DIR}/hw-components.log &
+
+cd /home/nos3/builds/nos3/sims/build/bin && \
+ ./nos3-single-simulator -f ./nos3-simulator.xml generic-eps-sim 2>&1 \
+ | tee -a ${DIR}/eps.log | tee -a ${DIR}/hw-components.log &
+
+cd /home/nos3/builds/nos3/sims/build/bin && \
+ ./nos3-single-simulator -f ./nos3-simulator.xml generic-fss-sim 2>&1 \
+ | tee -a ${DIR}/fss.log | tee -a ${DIR}/hw-components.log &
+
+cd /home/nos3/builds/nos3/sims/build/bin && \
+ ./nos3-single-simulator -f ./nos3-simulator.xml gps 2>&1 \
+ | tee -a ${DIR}/gps.log | tee -a ${DIR}/hw-components.log &
+
+cd /home/nos3/builds/nos3/sims/build/bin && \
+ ./nos3-single-simulator -f ./nos3-simulator.xml generic-imu-sim 2>&1 \
+ | tee -a ${DIR}/imu.log | tee -a ${DIR}/hw-components.log &
+
+cd /home/nos3/builds/nos3/sims/build/bin && \
+ ./nos3-single-simulator -f ./nos3-simulator.xml generic-mag-sim 2>&1 \
+ | tee -a ${DIR}/mag.log | tee -a ${DIR}/hw-components.log &
+
+cd /home/nos3/builds/nos3/sims/build/bin && \
+ ./nos3-single-simulator -f ./nos3-simulator.xml generic-reactionwheel-sim0 2>&1 \
+ | tee -a ${DIR}/rw0.log | tee -a ${DIR}/hw-components.log &
+
+cd /home/nos3/builds/nos3/sims/build/bin && \
+ ./nos3-single-simulator -f ./nos3-simulator.xml generic-reactionwheel-sim1 2>&1 \
+ | tee -a ${DIR}/rw1.log | tee -a ${DIR}/hw-components.log &
+
+cd /home/nos3/builds/nos3/sims/build/bin && \
+ ./nos3-single-simulator -f ./nos3-simulator.xml generic-reactionwheel-sim2 2>&1 \
+ | tee -a ${DIR}/rw2.log | tee -a ${DIR}/hw-components.log &
+
+cd /home/nos3/builds/nos3/sims/build/bin && \
+ ./nos3-single-simulator -f ./nos3-simulator.xml sample-sim 2>&1 \
+ | tee -a ${DIR}/sample-sim.log | tee -a ${DIR}/hw-components.log &
+
+cd /home/nos3/builds/nos3/sims/build/bin && \
+ ./nos3-single-simulator -f ./nos3-simulator.xml generic-star-tracker-sim 2>&1 \
+ | tee -a ${DIR}/star-tracker.log | tee -a ${DIR}/hw-components.log &
+
+cd /home/nos3/builds/nos3/sims/build/bin && \
+ ./nos3-single-simulator -f ./nos3-simulator.xml generic-thruster-sim 2>&1 \
+ | tee -a ${DIR}/thruster.log | tee -a ${DIR}/hw-components.log &
+
+cd /home/nos3/builds/nos3/sims/build/bin && \
+ ./nos3-single-simulator -f ./nos3-simulator.xml generic-torquer-sim 2>&1 \
+ | tee -a ${DIR}/torquer.log | tee -a ${DIR}/hw-components.log &
+
+# cd /home/nos3/builds/nos3/sims/build/bin && \
+# ./nos3-single-simulator -f ./nos3-simulator.xml truth42sim 2>&1 \
+# | tee -a ${DIR}/truth42.log | tee -a ${DIR}/hw-components.log &
+
+tail -f /dev/null
diff --git a/deployments/targets/kubernetes/Taskfile.yaml b/deployments/targets/kubernetes/Taskfile.yaml
new file mode 100644
index 000000000..7ee253871
--- /dev/null
+++ b/deployments/targets/kubernetes/Taskfile.yaml
@@ -0,0 +1,678 @@
+---
+version: '3'
+
+dotenv:
+ - ../../../targets/docker/.env
+
+# kind delete cluster --name ${K8S_CLUSTER} || true
+# kind create cluster --name ${K8S_CLUSTER} || true
+
+env:
+ KIND_EXPERIMENTAL_PROVIDER: '{{.KIND_EXPERIMENTAL_PROVIDER | default "docker"}}'
+
+vars:
+ TARGETS_DIR: ../../targets
+ SERVICES_DIR: ../../services
+ SCRIPTS_DIR: ../../scripts
+
+ CONTAINER_DIR: '{{.TARGETS_DIR}}/docker/'
+
+ PROJECT: 'nos3'
+ MISSION: 'm01'
+ SPACECRAFT: 'sc01'
+ SC_ENVIRO: 'sim'
+ COMPONENT: 'fortytwo'
+
+ ENVIRO_SCRIPT: '{{.SCRIPTS_DIR}}/env.sh'
+ ENVIRO_FILE: '{{.CONTAINER_DIR}}/.env'
+
+ DEFAULT_NAMESPACE: '{{.PROJECT}}-{{.MISSION}}-{{.SPACECRAFT}}'
+
+ APP_LABEL: '{{.DEFAULT_NAMESPACE}}-{{.COMPONENT}}'
+
+ # Context defaults - support both Docker Desktop and Podman
+ K8S_CONTEXT: '{{.K8S_CONTEXT | default "docker-desktop"}}'
+ K8S_CLUSTER: '{{.K8S_CLUSTER | default "docker-desktop"}}'
+ K8S_USER: '{{.K8S_USER | default "docker-desktop"}}'
+
+ # KIND provider - set to 'podman' to use Podman runtime with KIND
+ KIND_EXPERIMENTAL_PROVIDER: '{{.KIND_EXPERIMENTAL_PROVIDER | default "docker"}}'
+
+ K8S_APP_LABEL: '{{.APP_LABEL}}'
+ K8S_NAMESPACE: '{{.K8S_NAMESPACE | default .DEFAULT_NAMESPACE}}'
+
+ K8S_KUSTOMIZE_DIR: '{{.K8S_KUSTOMIZE_DIR | default "."}}'
+ K8S_PORT_FORWARD_VNC: '8080'
+ K8S_DEPLOYMENT_NAME: 'nos3-{{.COMPONENT}}'
+
+tasks:
+ default:
+ desc: Shows available tasks
+ cmds:
+ - task --list-all
+ silent: true
+
+ detect-runtime:
+ desc: Detect which container runtime is being used (docker or podman)
+ cmds:
+ - |
+ CURRENT_CONTEXT=$(kubectl config current-context 2>/dev/null || echo "none")
+ echo "Current kubectl context: $CURRENT_CONTEXT"
+ echo "KIND provider: {{.KIND_EXPERIMENTAL_PROVIDER}}"
+ echo ""
+
+ if echo "$CURRENT_CONTEXT" | grep -qi "podman"; then
+ echo "Detected runtime: Podman"
+ echo "To use Podman with all tasks, set: KIND_EXPERIMENTAL_PROVIDER=podman"
+ elif echo "$CURRENT_CONTEXT" | grep -qi "docker"; then
+ echo "Detected runtime: Docker Desktop"
+ echo "To use Docker with all tasks, set: KIND_EXPERIMENTAL_PROVIDER=docker (default)"
+ else
+ echo "Unknown runtime from context: $CURRENT_CONTEXT"
+ echo "Available runtimes:"
+ which docker 2>/dev/null && echo " - docker (installed)"
+ which podman 2>/dev/null && echo " - podman (installed)"
+ fi
+ echo ""
+ echo "Current KIND_EXPERIMENTAL_PROVIDER: {{.KIND_EXPERIMENTAL_PROVIDER}}"
+ silent: false
+
+ check-podman:
+ desc: Check if Podman Kubernetes is available and running
+ cmds:
+ - |
+ echo "Checking Podman Kubernetes setup..."
+
+ # Check if podman machine exists
+ if ! podman machine list 2>/dev/null | grep -q "Currently running"; then
+ echo "Warning: Podman machine is not running"
+ echo "Start it with: podman machine start"
+ exit 1
+ fi
+
+ echo "Podman machine is running"
+
+ # Check if podman-desktop context exists
+ if ! kubectl config get-contexts podman-desktop &>/dev/null; then
+ echo "Warning: podman-desktop context not found"
+ echo "Please ensure Kubernetes is enabled in Podman Desktop"
+ echo "Or manually create a context pointing to your Podman cluster"
+ exit 1
+ fi
+
+ echo "Podman Kubernetes context is available"
+ silent: false
+
+ get-contexts:
+ desc: List all available Kubernetes contexts
+ cmds:
+ - |
+ kubectl config get-contexts
+ silent: false
+
+ get-current-context:
+ desc: Show the current Kubernetes context
+ cmds:
+ - |
+ echo "Current context:"
+ kubectl config current-context
+ silent: false
+
+ set-context:
+ desc: Set the current Kubernetes context (use K8S_CONTEXT variable)
+ cmds:
+ - |
+ kubectl config use-context {{.K8S_CONTEXT}}
+ echo "Switched to context: {{.K8S_CONTEXT}}"
+ echo ""
+ echo "Common contexts:"
+ echo " - docker-desktop (Docker Desktop Kubernetes)"
+ echo " - podman-desktop (Podman Desktop Kubernetes)"
+ echo " - minikube (Minikube)"
+ echo ""
+ echo "Verify cluster: kubectl cluster-info"
+ silent: false
+
+ create-context:
+ desc: Create a new Kubernetes context
+ cmds:
+ - |
+ kubectl config set-context {{.K8S_CONTEXT}} \
+ --cluster={{.K8S_CLUSTER}} \
+ --user={{.K8S_USER}} \
+ --namespace={{.K8S_NAMESPACE}}
+ echo "Context {{.K8S_CONTEXT}} created/updated with:"
+ echo " Cluster: {{.K8S_CLUSTER}}"
+ echo " User: {{.K8S_USER}}"
+ echo " Namespace: {{.K8S_NAMESPACE}}"
+ - |
+ echo "To use this context, run: task set-context K8S_CONTEXT={{.K8S_CONTEXT}}"
+ silent: false
+
+ create-user-certs:
+ desc: Generate client certificates for a new user
+ vars:
+ NEW_USER: '{{.NEW_USER | default "myuser"}}'
+ CERT_DIR: '{{.CERT_DIR | default "/tmp/k8s-certs"}}'
+ ORG: '{{.ORG | default "system:masters"}}'
+ cmds:
+ - |
+ mkdir -p {{.CERT_DIR}}
+ echo "Generating private key for user: {{.NEW_USER}}"
+ openssl genrsa -out {{.CERT_DIR}}/{{.NEW_USER}}.key 2048
+ - |
+ echo "Generating certificate signing request (CSR)"
+ openssl req -new \
+ -key {{.CERT_DIR}}/{{.NEW_USER}}.key \
+ -out {{.CERT_DIR}}/{{.NEW_USER}}.csr \
+ -subj "/CN={{.NEW_USER}}/O={{.ORG}}"
+ - |
+ echo "Certificates generated in {{.CERT_DIR}}"
+ echo " Private key: {{.CERT_DIR}}/{{.NEW_USER}}.key"
+ echo " CSR: {{.CERT_DIR}}/{{.NEW_USER}}.csr"
+ echo ""
+ echo "Next steps:"
+ echo " 1. Get the CSR signed by the cluster CA"
+ echo " 2. Run: task create-user-kubeconfig NEW_USER={{.NEW_USER}} CERT_DIR={{.CERT_DIR}}"
+ silent: false
+
+ create-user-k8s-csr:
+ desc: Create and approve Kubernetes CertificateSigningRequest for user (may not work on Docker Desktop)
+ vars:
+ NEW_USER: '{{.NEW_USER | default "myuser"}}'
+ CERT_DIR: '{{.CERT_DIR | default "/tmp/k8s-certs"}}'
+ ORG: '{{.ORG | default "developers"}}'
+ cmds:
+ - |
+ echo "Creating Kubernetes CertificateSigningRequest for {{.NEW_USER}}"
+ cat < {{.CERT_DIR}}/{{.NEW_USER}}.crt
+ - |
+ echo "Certificate signed and saved to {{.CERT_DIR}}/{{.NEW_USER}}.crt"
+ echo ""
+ echo "Next step:"
+ echo " Run: task create-user-kubeconfig NEW_USER={{.NEW_USER}} CERT_DIR={{.CERT_DIR}}"
+ silent: false
+
+ create-user-manual-sign:
+ desc: Sign user certificate using cluster CA directly (for Docker Desktop)
+ vars:
+ NEW_USER: '{{.NEW_USER | default "myuser"}}'
+ CERT_DIR: '{{.CERT_DIR | default "/tmp/k8s-certs"}}'
+ CA_CERT: '{{.CA_CERT | default "$HOME/.minikube/ca.crt"}}'
+ CA_KEY: '{{.CA_KEY | default "$HOME/.minikube/ca.key"}}'
+ cmds:
+ - |
+ echo "Signing certificate for {{.NEW_USER}} using cluster CA"
+ echo "Note: This requires access to the cluster CA certificate and key"
+ echo ""
+
+ # For Docker Desktop, try to find CA files
+ if [ -f "$HOME/.docker/run/config/ca.pem" ]; then
+ CA_CERT="$HOME/.docker/run/config/ca.pem"
+ CA_KEY="$HOME/.docker/run/config/ca-key.pem"
+ fi
+
+ if [ ! -f "{{.CA_CERT}}" ]; then
+ echo "ERROR: CA certificate not found at {{.CA_CERT}}"
+ echo ""
+ echo "For Docker Desktop, CA files are typically not accessible."
+ echo "Recommended: Use the docker-desktop user instead, or:"
+ echo " 1. Use a cluster that supports CSR API (minikube, EKS, GKE, etc.)"
+ echo " 2. Manually copy CA files from the Docker Desktop VM"
+ exit 1
+ fi
+
+ openssl x509 -req \
+ -in {{.CERT_DIR}}/{{.NEW_USER}}.csr \
+ -CA {{.CA_CERT}} \
+ -CAkey {{.CA_KEY}} \
+ -CAcreateserial \
+ -out {{.CERT_DIR}}/{{.NEW_USER}}.crt \
+ -days 365
+ - |
+ echo "Certificate signed and saved to {{.CERT_DIR}}/{{.NEW_USER}}.crt"
+ echo ""
+ echo "Next step:"
+ echo " Run: task create-user-kubeconfig NEW_USER={{.NEW_USER}} CERT_DIR={{.CERT_DIR}}"
+ silent: false
+
+ create-user-kubeconfig:
+ desc: Add user credentials to kubeconfig
+ vars:
+ NEW_USER: '{{.NEW_USER | default "myuser"}}'
+ CERT_DIR: '{{.CERT_DIR | default "/tmp/k8s-certs"}}'
+ cmds:
+ - |
+ echo "Adding user {{.NEW_USER}} to kubeconfig"
+ kubectl config set-credentials {{.NEW_USER}} \
+ --client-certificate={{.CERT_DIR}}/{{.NEW_USER}}.crt \
+ --client-key={{.CERT_DIR}}/{{.NEW_USER}}.key \
+ --embed-certs=true
+ - |
+ echo "User {{.NEW_USER}} added to kubeconfig"
+ echo ""
+ echo "Create a context for this user:"
+ echo " task create-context K8S_USER={{.NEW_USER}} K8S_CONTEXT={{.NEW_USER}}-context"
+ echo ""
+ echo "Or use with existing context:"
+ echo " kubectl config set-context docker-desktop --user={{.NEW_USER}}"
+ silent: false
+
+ create-user:
+ desc: Complete workflow to create a new user (generates certs, signs via K8s CSR, adds to kubeconfig)
+ vars:
+ NEW_USER: '{{.NEW_USER | default "myuser"}}'
+ CERT_DIR: '{{.CERT_DIR | default "/tmp/k8s-certs"}}'
+ ORG: '{{.ORG | default "developers"}}'
+ cmds:
+ - task: create-user-certs
+ vars:
+ NEW_USER: '{{.NEW_USER}}'
+ CERT_DIR: '{{.CERT_DIR}}'
+ ORG: '{{.ORG}}'
+ - task: create-user-k8s-csr
+ vars:
+ NEW_USER: '{{.NEW_USER}}'
+ CERT_DIR: '{{.CERT_DIR}}'
+ ORG: '{{.ORG}}'
+ - task: create-user-kubeconfig
+ vars:
+ NEW_USER: '{{.NEW_USER}}'
+ CERT_DIR: '{{.CERT_DIR}}'
+ - |
+ echo ""
+ echo "User {{.NEW_USER}} created successfully with organization: {{.ORG}}"
+ echo ""
+ echo "IMPORTANT: The user has been created but has NO permissions yet."
+ echo "You need to create a RoleBinding or ClusterRoleBinding to grant permissions."
+ echo ""
+ echo "Example - Grant admin in a namespace:"
+ echo " kubectl create rolebinding {{.NEW_USER}}-admin --clusterrole=admin --user={{.NEW_USER}} -n {{.K8S_NAMESPACE}}"
+ echo ""
+ echo "Example - Grant cluster-wide view:"
+ echo " kubectl create clusterrolebinding {{.NEW_USER}}-view --clusterrole=view --user={{.NEW_USER}}"
+ echo ""
+ echo "To create a context for this user:"
+ echo " task create-context K8S_USER={{.NEW_USER}} K8S_CONTEXT={{.NEW_USER}}-context"
+ silent: false
+
+ list-users:
+ desc: List all users in kubeconfig
+ cmds:
+ - |
+ echo "Users in kubeconfig:"
+ kubectl config view -o jsonpath='{.users[*].name}' | tr ' ' '\n'
+ silent: false
+
+ delete-user:
+ desc: Remove user from kubeconfig
+ vars:
+ USER_TO_DELETE: '{{.USER_TO_DELETE | default "myuser"}}'
+ cmds:
+ - |
+ kubectl config unset users.{{.USER_TO_DELETE}}
+ echo "User {{.USER_TO_DELETE}} removed from kubeconfig"
+ - rm -rf /tmp/k8s-certs/{{.USER_TO_DELETE}}*
+ silent: false
+
+ create-namespace:
+ desc: Create the Kubernetes namespace
+ cmds:
+ - |
+ kubectl create namespace {{.K8S_NAMESPACE}} \
+ --context {{.K8S_CONTEXT}} || \
+ echo "Namespace {{.K8S_NAMESPACE}} already exists"
+ silent: false
+
+ create-configmap-from-env:
+ desc: Create a ConfigMap from the .env file
+ deps:
+ - create-namespace
+ vars:
+ CONFIGMAP_NAME: '{{.CONFIGMAP_NAME | default "env-configmap"}}'
+ cmds:
+ - |
+ echo "Creating ConfigMap {{.CONFIGMAP_NAME}} from {{.ENVIRO_FILE}}"
+ kubectl create configmap {{.CONFIGMAP_NAME}} \
+ --from-env-file={{.ENVIRO_FILE}} \
+ --context {{.K8S_CONTEXT}} \
+ --namespace={{.K8S_NAMESPACE}} \
+ --dry-run=client -o yaml > {{.CONFIGMAP_NAME}}.yaml
+ - echo "ConfigMap {{.CONFIGMAP_NAME}} created/updated successfully"
+ silent: false
+
+ apply-configmaps:
+ desc: Apply configmaps to the namespace
+ deps:
+ - create-namespace
+ cmds:
+ - |
+ kubectl apply -f configmap.yaml \
+ --context {{.K8S_CONTEXT}} \
+ --namespace={{.K8S_NAMESPACE}}
+ - echo "ConfigMaps applied successfully"
+ silent: false
+
+ apply-service:
+ desc: Apply service to the namespace
+ deps:
+ - create-namespace
+ cmds:
+ - |
+ kubectl apply -f service.yaml \
+ --context {{.K8S_CONTEXT}} \
+ --namespace={{.K8S_NAMESPACE}}
+ - echo "Service applied successfully"
+ silent: false
+
+ apply-kustomize:
+ desc: Apply kustomize to the namespace
+ deps:
+ - create-namespace
+ # - apply-configmaps
+ cmds:
+ - |
+ kubectl apply -k . \
+ --context {{.K8S_CONTEXT}} \
+ --namespace={{.K8S_NAMESPACE}}
+ - echo "Kustomization applied successfully"
+ silent: false
+
+ apply-deployment:
+ desc: Apply deployment to the namespace
+ deps:
+ - create-namespace
+ - apply-configmaps
+ cmds:
+ - |
+ kubectl apply -f deployment.yaml \
+ --context {{.K8S_CONTEXT}} \
+ --namespace={{.K8S_NAMESPACE}}
+ - echo "Deployment applied successfully"
+ silent: false
+
+ apply-all:
+ desc: Apply all Kubernetes resources (recommended)
+ deps:
+ - create-namespace
+ cmds:
+ - |
+ kubectl apply -k {{.K8S_KUSTOMIZE_DIR}} \
+ --context {{.K8S_CONTEXT}} \
+ --namespace={{.K8S_NAMESPACE}}
+ - task: port-forward
+ - echo "All resources applied successfully via kustomize"
+ silent: false
+
+ delete-deployment:
+ desc: Delete the deployment
+ cmds:
+ - |
+ kubectl delete -f deployment.yaml \
+ --context {{.K8S_CONTEXT}} \
+ --namespace={{.K8S_NAMESPACE}} || true
+ - echo "Deployment deleted"
+ silent: false
+
+ delete-namespace:
+ desc: Delete Namespace
+ cmds:
+ - |
+ kubectl delete namespace {{.K8S_NAMESPACE}} \
+ --context {{.K8S_CONTEXT}} || true
+ - echo "Namespace deleted"
+ silent: false
+
+ delete-service:
+ desc: Delete the service
+ cmds:
+ - |
+ kubectl delete -f service.yaml \
+ --context {{.K8S_CONTEXT}} \
+ --namespace={{.K8S_NAMESPACE}} || true
+ - echo "Service deleted"
+ silent: false
+
+ delete-configmaps:
+ desc: Delete the configmaps
+ cmds:
+ - |
+ kubectl delete -f configmap.yaml \
+ --context {{.K8S_CONTEXT}} \
+ --namespace={{.K8S_NAMESPACE}} || true
+ - echo "ConfigMaps deleted"
+ silent: false
+
+ delete-all:
+ desc: Delete all resources
+ deps:
+ - delete-deployment
+ - delete-service
+ - delete-configmaps
+ - delete-namespace
+ cmds:
+ - |
+ kubectl delete -k {{.K8S_KUSTOMIZE_DIR}} \
+ --context {{.K8S_CONTEXT}} || true
+ - echo "All resources deleted via kustomize"
+ silent: false
+
+ restart-deployment:
+ desc: Restart the deployment (rollout restart)
+ cmds:
+ - |
+ kubectl rollout restart deployment/nos3-fortytwo \
+ --context {{.K8S_CONTEXT}} \
+ --namespace={{.K8S_NAMESPACE}}
+ - echo "Deployment restarted"
+ silent: false
+
+ get-pods:
+ desc: Get pods in the namespace
+ cmds:
+ - |
+ kubectl get pods \
+ --context {{.K8S_CONTEXT}} \
+ --namespace={{.K8S_NAMESPACE}} \
+ -l app={{.K8S_APP_LABEL}}
+ silent: false
+
+ get-all:
+ desc: Get all resources in the namespace
+ cmds:
+ - |
+ kubectl get all \
+ --context {{.K8S_CONTEXT}} \
+ --namespace={{.K8S_NAMESPACE}} \
+ -l app={{.K8S_APP_LABEL}}
+ silent: false
+
+ describe-pod:
+ desc: Describe the fortytwo pod (useful for debugging)
+ cmds:
+ - |
+ POD_NAME=$(kubectl get pods \
+ --context {{.K8S_CONTEXT}} \
+ --namespace={{.K8S_NAMESPACE}} \
+ -l app={{.K8S_APP_LABEL}} \
+ -o jsonpath='{.items[0].metadata.name}')
+ kubectl describe pod $POD_NAME \
+ --context {{.K8S_CONTEXT}} \
+ --namespace={{.K8S_NAMESPACE}}
+ silent: false
+
+ logs:
+ desc: Get logs from the fortytwo pod
+ cmds:
+ - |
+ POD_NAME=$(kubectl get pods \
+ --context {{.K8S_CONTEXT}} \
+ --namespace={{.K8S_NAMESPACE}} \
+ -l app={{.K8S_APP_LABEL}} \
+ -o jsonpath='{.items[0].metadata.name}')
+ kubectl logs $POD_NAME \
+ --context {{.K8S_CONTEXT}} \
+ --namespace={{.K8S_NAMESPACE}} \
+ --tail=100 \
+ --follow
+ silent: false
+
+ logs-previous:
+ desc: Get logs from the previous container instance (useful after crash)
+ cmds:
+ - |
+ POD_NAME=$(kubectl get pods \
+ --context {{.K8S_CONTEXT}} \
+ --namespace={{.K8S_NAMESPACE}} \
+ -l app={{.K8S_APP_LABEL}} \
+ -o jsonpath='{.items[0].metadata.name}')
+ kubectl logs $POD_NAME \
+ --context {{.K8S_CONTEXT}} \
+ --namespace={{.K8S_NAMESPACE}} \
+ --previous
+ silent: false
+
+ exec:
+ desc: Execute a shell in the fortytwo pod
+ cmds:
+ - |
+ POD_NAME=$(kubectl get pods \
+ --context {{.K8S_CONTEXT}} \
+ --namespace={{.K8S_NAMESPACE}} \
+ -l app={{.K8S_APP_LABEL}} \
+ -o jsonpath='{.items[0].metadata.name}')
+ kubectl exec -it $POD_NAME \
+ --context {{.K8S_CONTEXT}} \
+ --namespace={{.K8S_NAMESPACE}} \
+ -- /bin/bash
+ silent: false
+
+ port-forward:
+ desc: Port forward to access services locally
+ cmds:
+ - |
+ echo "Checking for pod availability..."
+ POD_NAME=$(kubectl get pods \
+ --context {{.K8S_CONTEXT}} \
+ --namespace={{.K8S_NAMESPACE}} \
+ -l app={{.K8S_APP_LABEL}} \
+ -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)
+
+ if [ -z "$POD_NAME" ]; then
+ echo "Error: No pod found with label app={{.K8S_APP_LABEL}}"
+ exit 1
+ fi
+
+ echo "Found pod: $POD_NAME"
+ echo "Waiting for pod to be ready..."
+
+ kubectl wait --for=condition=ready pod/$POD_NAME \
+ --context {{.K8S_CONTEXT}} \
+ --namespace={{.K8S_NAMESPACE}} \
+ --timeout=300s
+
+ if [ $? -ne 0 ]; then
+ echo "Error: Pod did not become ready within timeout"
+ exit 1
+ fi
+
+ echo "Pod is ready!"
+ echo "Forwarding ports:"
+ echo " - VNC: http://localhost:{{.K8S_PORT_FORWARD_VNC}}"
+ kubectl port-forward ${POD_NAME} \
+ --context {{.K8S_CONTEXT}} \
+ --namespace={{.K8S_NAMESPACE}} \
+ {{.K8S_PORT_FORWARD_VNC}}:80
+ silent: false
+
+ status:
+ desc: Show status of all resources
+ cmds:
+ - echo "Namespace {{.K8S_NAMESPACE}} status:"
+ - echo ""
+ - echo "=== ConfigMaps ==="
+ - |
+ kubectl get configmaps \
+ --context {{.K8S_CONTEXT}} \
+ --namespace={{.K8S_NAMESPACE}} \
+ -l app={{.K8S_APP_LABEL}} || echo "No configmaps found"
+ - echo ""
+ - echo "=== Services ==="
+ - |
+ kubectl get services \
+ --context {{.K8S_CONTEXT}} \
+ --namespace={{.K8S_NAMESPACE}} \
+ fortytwo || echo "No services found"
+ - echo ""
+ - echo "=== Deployments ==="
+ - |
+ kubectl get deployments \
+ --context {{.K8S_CONTEXT}} \
+ --namespace={{.K8S_NAMESPACE}} \
+ -l app={{.K8S_APP_LABEL}} || echo "No deployments found"
+ - echo ""
+ - echo "=== Pods ==="
+ - |
+ kubectl get pods \
+ --context {{.K8S_CONTEXT}} \
+ --namespace={{.K8S_NAMESPACE}} \
+ -l app={{.K8S_APP_LABEL}} || echo "No pods found"
+ silent: false
+
+ redeploy:
+ desc: Delete and recreate all resources (full redeploy)
+ cmds:
+ - task: delete-all
+ - sleep 3
+ - task: apply-all
+ - task: get-pods
+ silent: false
+
+ troubleshoot:
+ desc: Run troubleshooting diagnostics
+ cmds:
+ - echo "=== Checking namespace ==="
+ - |
+ kubectl get namespace {{.K8S_NAMESPACE}} \
+ --context {{.K8S_CONTEXT}} || echo "Namespace does not exist!"
+ - echo ""
+ - echo "=== Checking configmaps ==="
+ - |
+ kubectl get configmaps nos3-fortytwo-cm0 \
+ --context {{.K8S_CONTEXT}} \
+ --namespace={{.K8S_NAMESPACE}} || echo "ConfigMaps not found!"
+ - echo ""
+ - echo "=== Checking pod events ==="
+ - |
+ POD_NAME=$(kubectl get pods \
+ --context {{.K8S_CONTEXT}} \
+ --namespace={{.K8S_NAMESPACE}} \
+ -l app={{.K8S_APP_LABEL}} \
+ -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)
+ if [ -n "$POD_NAME" ]; then
+ kubectl get events \
+ --context {{.K8S_CONTEXT}} \
+ --namespace={{.K8S_NAMESPACE}} \
+ --field-selector involvedObject.name=$POD_NAME \
+ --sort-by='.lastTimestamp'
+ else
+ echo "No pods found"
+ fi
+ silent: false
diff --git a/deployments/targets/kubernetes/fortytwo/configmaps/args-configmap.yaml b/deployments/targets/kubernetes/fortytwo/configmaps/args-configmap.yaml
new file mode 100644
index 000000000..e69de29bb
diff --git a/deployments/targets/kubernetes/fortytwo/configmaps/env-configmap.yaml b/deployments/targets/kubernetes/fortytwo/configmaps/env-configmap.yaml
new file mode 100644
index 000000000..5b7e71ef5
--- /dev/null
+++ b/deployments/targets/kubernetes/fortytwo/configmaps/env-configmap.yaml
@@ -0,0 +1,65 @@
+apiVersion: v1
+
+kind: ConfigMap
+
+metadata:
+ name: nos3-m01-sc01-fortytwo-env
+
+data:
+ BASE_DIR: /opt/nasa-itc
+ COMPONENT_DIR: /home/nos3/builds/nos3/components
+ COMPOSE_PROJECT_NAME: nos3-m01-sc01
+ DEPLOYMENT_ENVIRO: ""
+ FORTYTWO_BASE_DIR: /opt/nasa-itc
+ FORTYTWO_DISPLAY: :1
+ FORTYTWO_GIT_COMMIT: nos3-main
+ FORTYTWO_GIT_FOLDER: "42"
+ FORTYTWO_GIT_URL: https://github.com/nasa-itc/42.git
+ FORTYTWO_LEAP_SECONDS: "37"
+ FORTYTWO_PORT: "30090"
+ FORTYTWO_RECOMPILE: "false"
+ FORTYTWO_SIM_DATE: '"12 29 2025"'
+ FORTYTWO_SIM_TIME: '"15 57 13.00"'
+ FORTYTWO_STARTUP_FOLDER: NOS3InOut
+ FORTYTWO_VNC_PASSWORD: c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2
+ FSW_SOFTWARE: cfs
+ GSW_SOFTWARE: yamcs
+ HEALTHCHECK_PORT: ""
+ HTTP_PROXY: ""
+ HTTPS_PROXY: ""
+ J2000_EPOCH_SECONDS: "946728000"
+ J2000_REFERENCE_DATETIME: '"2000-01-01 12:00:00 UTC"'
+ MAVEN_HTTPS_PROXY: '"--settings ./settings.xml"'
+ MAVEN_REPO_LOCAL: /home/nos3/.nos3/.m2
+ MAVEN_SETTINGS_FILE: ""
+ MISSION: m01
+ NO_PROXY: ""
+ NOS3_BASE_DIR: /home/nos3/.nos3
+ NOS3_CFG_PATH: ..
+ NOS3_DIR: /home/nos3/.nos3
+ NOS3_GIT_COMMIT: dev
+ NOS3_GIT_URL: https://github.com/nasa/nos3
+ NOS3_IMAGE_URI: docker.io/ivvitc/nos3-64:20251107
+ NOS3_MISSION_CFG_FILE: ../cfg/nos3-mission.xml
+ NOS3_USER: nos3
+ OPENMCT_GIT_COMMIT: master
+ OPENMCT_GIT_URL: https://github.com/akhenry/openmct-yamcs.git
+ OPENMCT_IMAGE_URI: docker.io/library/ubuntu:25.04
+ OPENMCT_NODE_VERSION: v21.7.3
+ OPENMCT_NVM_VERSION: v0.40.1
+ OPENMCT_PORT: "9000"
+ PROJECT_NAME: nos3-m01-sc01
+ SIM_T0_DATETIME: '"2025-12-29 15:56:43 UTC"'
+ SIM_T0_EPOCH_MILLISECONDS: "1767023833000"
+ SIM_T0_EPOCH_SECONDS: "1767023833"
+ SIM_TF_EPOCH_MILLISECONDS: "1767027433000"
+ SPACECRAFT: sc01
+ START_TIME: "820295833"
+ TIME_INTERVAL: "3600"
+ TIME_INTERVAL_MILLISECONDS: "3600000"
+ TIME_OFFSET_SECONDS: "+30"
+ YAMCS_DATA_DIR: /home/nos3/.nos3/yamcs/target/yamcs/yamcs-data
+ YAMCS_GIT_COMMIT: nos3-dev
+ YAMCS_GIT_URL: https://github.com/nasa-itc/nos3_yamcs_master.git
+ YAMCS_INSTANCES: nos3
+ YAMCS_PORT: "8090"
diff --git a/deployments/targets/kubernetes/fortytwo/configmaps/volumes-configmap.yaml b/deployments/targets/kubernetes/fortytwo/configmaps/volumes-configmap.yaml
new file mode 100644
index 000000000..e82b4932f
--- /dev/null
+++ b/deployments/targets/kubernetes/fortytwo/configmaps/volumes-configmap.yaml
@@ -0,0 +1,53 @@
+---
+apiVersion: v1
+
+kind: ConfigMap
+metadata:
+ annotations:
+ use-subpath: "true"
+ name: nos3-m01-sc01-fortytwo-volumes
+
+data:
+ entrypoint.sh: |
+ #!/bin/bash
+
+ rm -f /tmp/.X1-lock || true
+ rm -f /tmp/.X11-unix/X1 || true
+
+ pkill -9 -f vncserver || true
+ pkill -9 -f Xvnc || true
+ pkill -9 -f websockify || true
+
+ /opt/TurboVNC/bin/vncserver -securitytypes tlsnone,x509none,none && \
+ sleep 5 && \
+ websockify -D \
+ --web=/usr/share/novnc/ \
+ --cert=~/novnc.pem 80 localhost:5901
+
+ export DISPLAY=${DISPLAY:-:1}
+ export DIR=/opt/nasa-itc
+ export GIT_FOLDER=${GIT_FOLDER}
+
+ xterm &
+
+ rm -rf /opt/nasa-itc/42/NO3InOut/{*.42,*.csv}
+
+ if [ "$RECOMPILE" == "true" ]; then
+ cd /opt/nasa-itc/42 && \
+ git fetch && \
+ git checkout ${GIT_COMMIT} && \
+ git pull origin && \
+ git submodule update && \
+ make clean && \
+ make -j2
+ fi
+
+ STARTUP_FOLDER=${STARTUP_FOLDER:-NO3InOut}
+
+ cd /opt/nasa-itc/42 && \
+ xterm -e "./42 ${STARTUP_FOLDER}" &
+
+ echo "Started 42 with PID $!"
+
+ tail -f /dev/null
+
diff --git a/deployments/targets/kubernetes/fortytwo/deployment.yaml b/deployments/targets/kubernetes/fortytwo/deployment.yaml
new file mode 100644
index 000000000..35bea97d1
--- /dev/null
+++ b/deployments/targets/kubernetes/fortytwo/deployment.yaml
@@ -0,0 +1,58 @@
+---
+apiVersion: apps/v1
+
+kind: Deployment
+
+metadata:
+ name: nos3-m01-sc01-fortytwo
+ labels:
+ app: nos3-m01-sc01-fortytwo
+
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: nos3-m01-sc01-fortytwo
+ template:
+ metadata:
+ labels:
+ app: nos3-m01-sc01-fortytwo
+ spec:
+ containers:
+ - args:
+ - /entrypoint.sh
+ env:
+ - name: COMPONENT_NAME
+ value: fortytwo
+ - name: DISPLAY
+ value: :1
+ image: ghcr.io/haisamido/nos3-base-fortytwo:dev
+ workingDir: /42
+ imagePullPolicy: Always
+ name: nos3-m01-sc01-fortytwo
+ ports:
+ - containerPort: 80
+ protocol: TCP
+ resources:
+ limits:
+ cpu: "3"
+ memory: "2048Mi"
+ requests:
+ cpu: "1"
+ memory: "2048Mi"
+ securityContext:
+ privileged: true
+ volumeMounts:
+ - mountPath: /entrypoint.sh
+ name: nos3-m01-sc01-fortytwo-volumes
+ subPath: entrypoint.sh
+ hostname: fortytwo
+ restartPolicy: Always
+ volumes:
+ - configMap:
+ items:
+ - key: entrypoint.sh
+ path: entrypoint.sh
+ name: nos3-m01-sc01-fortytwo-volumes
+ defaultMode: 0555
+ name: nos3-m01-sc01-fortytwo-volumes
diff --git a/deployments/targets/kubernetes/fortytwo/ingress.yaml b/deployments/targets/kubernetes/fortytwo/ingress.yaml
new file mode 100644
index 000000000..e69de29bb
diff --git a/deployments/targets/kubernetes/fortytwo/kustomization.yaml b/deployments/targets/kubernetes/fortytwo/kustomization.yaml
new file mode 100644
index 000000000..3d7dc0f0e
--- /dev/null
+++ b/deployments/targets/kubernetes/fortytwo/kustomization.yaml
@@ -0,0 +1,10 @@
+---
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+resources:
+ - ./configmaps/args-configmap.yaml
+ - ./configmaps/env-configmap.yaml
+ - ./configmaps/volumes-configmap.yaml
+ - ./deployment.yaml
+ - ./service.yaml
+ - ./ingress.yaml
diff --git a/deployments/targets/kubernetes/fortytwo/service.yaml b/deployments/targets/kubernetes/fortytwo/service.yaml
new file mode 100644
index 000000000..234314595
--- /dev/null
+++ b/deployments/targets/kubernetes/fortytwo/service.yaml
@@ -0,0 +1,17 @@
+---
+apiVersion: v1
+
+kind: Service
+
+metadata:
+ name: nos3-m01-sc01-fortytwo
+
+spec:
+ selector:
+ app: nos3-m01-sc01-fortytwo
+ ports:
+ - name: x-vnc
+ protocol: TCP
+ port: 80
+ targetPort: 80
+ type: ClusterIP
diff --git a/deployments/targets/kubernetes/fsw/configmap.yaml b/deployments/targets/kubernetes/fsw/configmap.yaml
new file mode 100644
index 000000000..020855f91
--- /dev/null
+++ b/deployments/targets/kubernetes/fsw/configmap.yaml
@@ -0,0 +1,15 @@
+---
+apiVersion: v1
+data:
+ entrypoint_fsw.sh: |
+ #!/bin/bash
+ pkill -f fsw_respawn.sh || true
+ pkill -f core-cpu1 || true
+ $SCRIPT_DIR/fsw/fsw_respawn.sh
+kind: ConfigMap
+metadata:
+ annotations:
+ use-subpath: "true"
+ labels:
+ io.kompose.service: nos3-fsw
+ name: nos3-fsw-cm0
diff --git a/deployments/targets/kubernetes/fsw/deployment.yaml b/deployments/targets/kubernetes/fsw/deployment.yaml
new file mode 100644
index 000000000..43b18399c
--- /dev/null
+++ b/deployments/targets/kubernetes/fsw/deployment.yaml
@@ -0,0 +1,67 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ namespace: nos3-m01-sc01
+ name: nos3-fsw
+ labels:
+ app: nos3-m01-sc01-fsw
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: nos3-m01-sc01-fsw
+ template:
+ metadata:
+ labels:
+ app: nos3-m01-sc01-fsw
+ spec:
+ containers:
+ - args:
+ - /entrypoint.sh
+ env:
+ - name: BASE_DIR
+ value: /builds/nos3
+ - name: CFG_FILE
+ value: /builds/nos3/sims/build/bin/nos_engine_server_config.json
+ - name: COMPONENT_NAME
+ value: fsw
+ - name: HEALTHCHECK_PORT
+ value: "60000"
+ - name: LOG_CONFIG
+ value: /builds/nos3/sims/build/bin/sim_log_config.xml
+ - name: SCRIPT_DIR
+ value: /builds/nos3/scripts
+ - name: SIMULATOR_BIN
+ value: /usr/bin/nos_engine_server_standalone
+ - name: SIM_BIN
+ value: /builds/nos3/sims/build/bin
+ image: registry.appdat.jsc.nasa.gov/ssmo/images/ssmo/nos3/nos3-base:20250217
+ imagePullPolicy: IfNotPresent
+ name: 01-sc01-nos-fsw
+ resources:
+ limits:
+ cpu: 100m
+ memory: "128Mi"
+ requests:
+ cpu: 100m
+ memory: "128Mi"
+ securityContext:
+ capabilities:
+ add:
+ - sys_nice
+ privileged: true
+ volumeMounts:
+ - mountPath: /entrypoint.sh
+ name: nos3-fsw-cm0
+ subPath: entrypoint.sh
+ workingDir: /builds/nos3/fsw/build/exe/cpu1
+ hostname: fsw
+ restartPolicy: Always
+ volumes:
+ - configMap:
+ items:
+ - key: entrypoint_fsw.sh
+ path: entrypoint.sh
+ name: nos3-fsw-cm0
+ defaultMode: 0555
+ name: nos3-fsw-cm0
diff --git a/deployments/targets/kubernetes/fsw/kustomization.yaml b/deployments/targets/kubernetes/fsw/kustomization.yaml
new file mode 100644
index 000000000..164db37ac
--- /dev/null
+++ b/deployments/targets/kubernetes/fsw/kustomization.yaml
@@ -0,0 +1,7 @@
+---
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+resources:
+ - ./configmap.yaml
+ - ./deployment.yaml
+ - ./service.yaml
diff --git a/deployments/targets/kubernetes/fsw/service.yaml b/deployments/targets/kubernetes/fsw/service.yaml
new file mode 100644
index 000000000..38a3f4928
--- /dev/null
+++ b/deployments/targets/kubernetes/fsw/service.yaml
@@ -0,0 +1,19 @@
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: fsw
+ namespace: nos3-m01-sc01
+spec:
+ selector:
+ app: nos3-m01-sc01-fsw
+ ports:
+ - name: healthcheck-tcp
+ protocol: TCP
+ port: 60000
+ targetPort: 60000
+ - name: healthcheck-udp
+ protocol: UDP
+ port: 60000
+ targetPort: 60000
+ type: ClusterIP
diff --git a/deployments/targets/kubernetes/gps/deployment.yaml b/deployments/targets/kubernetes/gps/deployment.yaml
new file mode 100644
index 000000000..651d55ef8
--- /dev/null
+++ b/deployments/targets/kubernetes/gps/deployment.yaml
@@ -0,0 +1,47 @@
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ namespace: nos3-m01-sc01
+ name: nos3-gps
+ labels:
+ app: nos3-m01-sc01-gps
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: nos3-m01-sc01-gps
+ template:
+ metadata:
+ labels:
+ app: nos3-m01-sc01-gps
+ spec:
+ containers:
+ - env:
+ - name: BASE_DIR
+ value: /builds/nos3
+ - name: CFG_FILE
+ value: /builds/nos3/sims/build/bin/nos3-simulator.xml
+ - name: COMPONENT_NAME
+ value: gps
+ - name: HEALTHCHECK_PORT
+ value: "60000"
+ - name: LOG_CONFIG
+ value: /builds/nos3/sims/build/bin/sim_log_config.xml
+ - name: SIMULATOR_BIN
+ value: /builds/nos3/sims/build/bin/nos3-single-simulator
+ - name: SIM_BIN
+ value: /builds/nos3/sims/build/bin
+ image: registry.appdat.jsc.nasa.gov/ssmo/images/ssmo/nos3/nos3-base:20250217
+ name: 03-sc01-gps-sim
+ resources:
+ limits:
+ cpu: 100m
+ memory: "128Mi"
+ requests:
+ cpu: 100m
+ memory: "Mi"
+ securityContext:
+ privileged: true
+ hostname: gps
+ restartPolicy: Always
diff --git a/deployments/targets/kubernetes/gps/kustomization.yaml b/deployments/targets/kubernetes/gps/kustomization.yaml
new file mode 100644
index 000000000..318bab171
--- /dev/null
+++ b/deployments/targets/kubernetes/gps/kustomization.yaml
@@ -0,0 +1,7 @@
+---
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+resources:
+# - ./configmap.yaml
+ - ./deployment.yaml
+ - ./service.yaml
diff --git a/deployments/targets/kubernetes/gps/service.yaml b/deployments/targets/kubernetes/gps/service.yaml
new file mode 100644
index 000000000..338bbb405
--- /dev/null
+++ b/deployments/targets/kubernetes/gps/service.yaml
@@ -0,0 +1,19 @@
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: gps
+ namespace: nos3-m01-sc01
+spec:
+ selector:
+ app: nos3-m01-sc01-gps
+ ports:
+ - name: healthcheck-tcp
+ protocol: TCP
+ port: 60000
+ targetPort: 60000
+ - name: healthcheck-udp
+ protocol: UDP
+ port: 60000
+ targetPort: 60000
+ type: ClusterIP
diff --git a/deployments/targets/kubernetes/kubernetes.sh b/deployments/targets/kubernetes/kubernetes.sh
new file mode 100755
index 000000000..37bceb796
--- /dev/null
+++ b/deployments/targets/kubernetes/kubernetes.sh
@@ -0,0 +1,29 @@
+
+REALM=nos3
+#K8S_CONTEXT=kind-${REALM}
+
+# kompose convert --file ../../docker/docker-compose.yaml
+
+export K8S_CONTEXT=docker-desktop
+export K8S_NAMESPACE=${REALM}-m01-sc01
+
+COMPONENT=fortytwo
+
+echo; echo Creating namespace ${K8S_NAMESPACE} in context ${K8S_CONTEXT}...
+kubectl create namespace ${K8S_NAMESPACE} --context ${K8S_CONTEXT} || true
+
+kubectl --context ${K8S_CONTEXT} --namespace ${K8S_NAMESPACE} apply -k .
+
+echo
+echo "Waiting on fortytwo pod to be ready"
+echo
+sleep 10
+
+K8S_POD=$(kubectl get pods --context ${K8S_CONTEXT} --namespace ${K8S_NAMESPACE} | grep ${COMPONENT} | cut -d' ' -f1)
+
+x=${HOME}/development/github.com/haisamido/nos3/cfg/InOut
+
+kubectl --context ${K8S_CONTEXT} --namespace ${K8S_NAMESPACE} cp ${x} ${K8S_POD}:/opt/nasa-itc/42/X
+
+sleep 10
+kubectl port-forward pod/${K8S_POD} 30003:80 --context ${K8S_CONTEXT} --namespace ${K8S_NAMESPACE}
diff --git a/deployments/targets/kubernetes/kustomization.yaml b/deployments/targets/kubernetes/kustomization.yaml
new file mode 100644
index 000000000..58106a6c3
--- /dev/null
+++ b/deployments/targets/kubernetes/kustomization.yaml
@@ -0,0 +1,10 @@
+---
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+resources:
+ - ./fortytwo
+ # - ./fsw
+ # - ./nos-engine-server
+ # - ./gps
+ # - ./time
+
\ No newline at end of file
diff --git a/deployments/targets/kubernetes/nos-engine-server/configmap.yaml b/deployments/targets/kubernetes/nos-engine-server/configmap.yaml
new file mode 100644
index 000000000..05ae714ef
--- /dev/null
+++ b/deployments/targets/kubernetes/nos-engine-server/configmap.yaml
@@ -0,0 +1,14 @@
+---
+apiVersion: v1
+data:
+ entrypoint_nos_engine_server.sh: |
+ #!/bin/bash
+ pkill -f nos_engine_server_standalone || true
+ /usr/bin/nos_engine_server_standalone /builds/nos3/sims/build/bin/nos_engine_server_config.json
+kind: ConfigMap
+metadata:
+ annotations:
+ use-subpath: "true"
+ labels:
+ io.kompose.service: nos3-nos-engine-server
+ name: nos3-nos-engine-server-cm0
diff --git a/deployments/targets/kubernetes/nos-engine-server/deployment.yaml b/deployments/targets/kubernetes/nos-engine-server/deployment.yaml
new file mode 100644
index 000000000..2b3e4e464
--- /dev/null
+++ b/deployments/targets/kubernetes/nos-engine-server/deployment.yaml
@@ -0,0 +1,68 @@
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ namespace: nos3-m01-sc01
+ name: nos3-nos-engine-server
+ labels:
+ app: nos3-m01-sc01-nos-engine-server
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: nos3-m01-sc01-nos-engine-server
+ template:
+ metadata:
+ labels:
+ app: nos3-m01-sc01-nos-engine-server
+ spec:
+ containers:
+ - args:
+ - /entrypoint.sh
+ env:
+ - name: BASE_DIR
+ value: /builds/nos3
+ - name: CFG_FILE
+ value: /builds/nos3/sims/build/bin/nos_engine_server_config.json
+ - name: HEALTHCHECK_PORT
+ value: "60000"
+ - name: LOG_CONFIG
+ value: /builds/nos3/sims/build/bin/sim_log_config.xml
+ - name: SIMULATOR_BIN
+ value: /usr/bin/nos_engine_server_standalone
+ - name: SIM_BIN
+ value: /builds/nos3/sims/build/bin
+ image: registry.appdat.jsc.nasa.gov/ssmo/images/ssmo/nos3/nos3-base:20250217
+ imagePullPolicy: IfNotPresent
+ name: 02-sc01-nos-engine-server
+ ports:
+ - containerPort: 12000
+ protocol: TCP
+ - containerPort: 12001
+ protocol: TCP
+ resources:
+ limits:
+ cpu: 100m
+ memory: "128Mi"
+ requests:
+ cpu: 100m
+ memory: "128Mi"
+ securityContext:
+ privileged: true
+ stdin: true
+ tty: true
+ volumeMounts:
+ - mountPath: /entrypoint.sh
+ name: nos3-nos-engine-server-cm0
+ subPath: entrypoint.sh
+ workingDir: /builds/nos3/sims/build/bin
+ hostname: nos-engine-server
+ restartPolicy: Always
+ volumes:
+ - configMap:
+ items:
+ - key: entrypoint_nos_engine_server.sh
+ path: entrypoint.sh
+ defaultMode: 0555
+ name: nos3-nos-engine-server-cm0
+ name: nos3-nos-engine-server-cm0
diff --git a/deployments/targets/kubernetes/nos-engine-server/kustomization.yaml b/deployments/targets/kubernetes/nos-engine-server/kustomization.yaml
new file mode 100644
index 000000000..164db37ac
--- /dev/null
+++ b/deployments/targets/kubernetes/nos-engine-server/kustomization.yaml
@@ -0,0 +1,7 @@
+---
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+resources:
+ - ./configmap.yaml
+ - ./deployment.yaml
+ - ./service.yaml
diff --git a/deployments/targets/kubernetes/nos-engine-server/service.yaml b/deployments/targets/kubernetes/nos-engine-server/service.yaml
new file mode 100644
index 000000000..dd2ed634b
--- /dev/null
+++ b/deployments/targets/kubernetes/nos-engine-server/service.yaml
@@ -0,0 +1,28 @@
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: nos-engine-server
+ namespace: nos3-m01-sc01
+spec:
+ selector:
+ app: nos3-m01-sc01-nos-engine-server
+ ports:
+ - name: fsw-tcp
+ protocol: TCP
+ port: 12000
+ targetPort: 12000
+ - name: nos3-tcp
+ protocol: TCP
+ port: 12001
+ targetPort: 12001
+ - name: healthcheck-tcp
+ protocol: TCP
+ port: 60000
+ targetPort: 60000
+ - name: healthcheck-udp
+ protocol: UDP
+ port: 60000
+ targetPort: 60000
+ type: ClusterIP
+
diff --git a/deployments/targets/kubernetes/time/deployment.yaml b/deployments/targets/kubernetes/time/deployment.yaml
new file mode 100644
index 000000000..dabe9a4a0
--- /dev/null
+++ b/deployments/targets/kubernetes/time/deployment.yaml
@@ -0,0 +1,50 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ annotations:
+ kompose.cmd: kompose convert --file ../../docker/docker-compose.yaml
+ kompose.version: 1.36.0 (HEAD)
+ labels:
+ io.kompose.service: nos3-time
+ name: nos3-time
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: nos3-m01-sc01-time
+ template:
+ metadata:
+ labels:
+ app: nos3-m01-sc01-time
+ spec:
+ containers:
+ - env:
+ - name: BASE_DIR
+ value: /builds/nos3
+ - name: CFG_FILE
+ value: /builds/nos3/sims/build/bin/nos3-simulator.xml
+ - name: COMPONENT_NAME
+ value: time
+ - name: HEALTHCHECK_PORT
+ value: "60000"
+ - name: LOG_CONFIG
+ value: /builds/nos3/sims/build/bin/sim_log_config.xml
+ - name: SIMULATOR_BIN
+ value: /builds/nos3/sims/build/bin/nos3-single-simulator
+ - name: SIM_BIN
+ value: /builds/nos3/sims/build/bin
+ image: registry.appdat.jsc.nasa.gov/ssmo/images/ssmo/nos3/nos3-base:20250217
+ name: 04-nos-time-driver
+ resources:
+ limits:
+ cpu: 100m
+ memory: "128Mi"
+ requests:
+ cpu: 100m
+ memory: "128Mi"
+ securityContext:
+ privileged: true
+ stdin: true
+ tty: true
+ hostname: time
+ restartPolicy: Always
diff --git a/deployments/targets/kubernetes/time/kustomization.yaml b/deployments/targets/kubernetes/time/kustomization.yaml
new file mode 100644
index 000000000..318bab171
--- /dev/null
+++ b/deployments/targets/kubernetes/time/kustomization.yaml
@@ -0,0 +1,7 @@
+---
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+resources:
+# - ./configmap.yaml
+ - ./deployment.yaml
+ - ./service.yaml
diff --git a/deployments/targets/kubernetes/time/service.yaml b/deployments/targets/kubernetes/time/service.yaml
new file mode 100644
index 000000000..81953c575
--- /dev/null
+++ b/deployments/targets/kubernetes/time/service.yaml
@@ -0,0 +1,19 @@
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: time
+ namespace: nos3-m01-sc01
+spec:
+ selector:
+ app: nos3-m01-sc01-time
+ ports:
+ - name: healthcheck-tcp
+ protocol: TCP
+ port: 60000
+ targetPort: 60000
+ - name: healthcheck-udp
+ protocol: UDP
+ port: 60000
+ targetPort: 60000
+ type: ClusterIP
diff --git a/deployments/templates/env.template b/deployments/templates/env.template
new file mode 100755
index 000000000..29cf7a242
--- /dev/null
+++ b/deployments/templates/env.template
@@ -0,0 +1,158 @@
+#!/usr/bin/env bash
+
+set -e
+
+# A script to generate .env file for container deployments
+
+# Simulation specific inputs
+NOW=$(date -u +"%Y-%m-%d %H:%M:%S UTC")
+SIM_T0_DATETIME="2026-01-01 00:00:00 UTC"
+SIM_T0_DATETIME="${NOW}"
+
+TIME_INTERVAL=3600 # 1 hour in seconds
+TIME_INTERVAL_MILLISECONDS=$( expr ${TIME_INTERVAL} \* 1000 )
+
+#SIM_T0_DATETIME="${NOW}"
+LEAP_SECONDS=37
+
+GSW_SOFTWARE=yamcs
+FSW_SOFTWARE=cfs
+
+# This is to define either the standard path, i.e. ${PWD}/nos3, or a local definition
+NOS3_CFG_PATH=..
+NOS3_MISSION_CFG_FILE=../cfg/nos3-mission.xml
+
+NOS3_GIT_URL=https://github.com/nasa/nos3
+NOS3_GIT_COMMIT=dev
+
+NOS3_IMAGE_URI=docker.io/ivvitc/nos3-64:20251107
+NOS3_BASE_DIR=/home/nos3/.nos3
+
+########################################################################
+# Avoid updating the below variables unless you know what you are doing
+########################################################################
+
+# Time specific derivations
+TIME_OFFSET_SECONDS=+30 # to simulate 30 seconds into the future from SIM_T0_DATETIME
+J2000_REFERENCE_DATETIME="2000-01-01 12:00:00 UTC"
+SIM_T0_EPOCH_SECONDS=$(date -ud "${SIM_T0_DATETIME} ${TIME_OFFSET_SECONDS} seconds" +"%s")
+SIM_T0_EPOCH_MILLISECONDS=$( expr ${SIM_T0_EPOCH_SECONDS} \* 1000 )
+SIM_TF_EPOCH_MILLISECONDS=$( expr ${SIM_T0_EPOCH_MILLISECONDS} + ${TIME_INTERVAL} \* 1000 )
+J2000_EPOCH_SECONDS=$(date -ud "${J2000_REFERENCE_DATETIME}" +"%s")
+
+cat << EOF
+#
+# Auto-generated by ${PWD}/$(basename $0) on $(date -u +"%Y-%m-%dT%H:%M:%S%z")
+#
+
+PROJECT=${PROJECT}
+FLEET=${FLEET}
+MISSION=${MISSION}
+SPACECRAFT=$SPACECRAFT
+
+PROJECT_NAME=${PROJECT_NAME}
+PROJECT_MISSION=${PROJECT_MISSION}
+COMPOSE_PROJECT_NAME=${COMPOSE_PROJECT_NAME}
+
+SC_ENVIRO=${SC_ENVIRO}
+
+# nos3 specific configurations
+NOS3_GIT_URL=${NOS3_GIT_URL}
+NOS3_GIT_COMMIT=${NOS3_GIT_COMMIT}
+NOS3_DIR=/home/nos3/.nos3
+NOS3_USER=nos3
+NOS3_IMAGE_URI=${NOS3_IMAGE_URI}
+NOS3_BASE_DIR=${NOS3_BASE_DIR}
+
+# This is to define either the standard path, i.e. ${PWD}/nos3, or a local definition
+NOS3_CFG_PATH=${NOS3_CFG_PATH}
+NOS3_MISSION_CFG_FILE=${NOS3_MISSION_CFG_FILE}
+
+# Simulation specific configurations
+SIM_T0_DATETIME="${SIM_T0_DATETIME}"
+J2000_REFERENCE_DATETIME="${J2000_REFERENCE_DATETIME}"
+
+TIME_OFFSET_SECONDS=${TIME_OFFSET_SECONDS}
+TIME_INTERVAL=${TIME_INTERVAL}
+TIME_INTERVAL_MILLISECONDS=${TIME_INTERVAL_MILLISECONDS}
+
+# In epoch seconds
+SIM_T0_EPOCH_SECONDS=${SIM_T0_EPOCH_SECONDS}
+SIM_T0_EPOCH_MILLISECONDS=${SIM_T0_EPOCH_MILLISECONDS}
+SIM_TF_EPOCH_MILLISECONDS=${SIM_TF_EPOCH_MILLISECONDS}
+J2000_EPOCH_SECONDS=${J2000_EPOCH_SECONDS}
+
+# In J2000 seconds
+START_TIME=$((${SIM_T0_EPOCH_SECONDS}-${J2000_EPOCH_SECONDS}))
+
+# Deployment specific configurations
+DEPLOYMENT_ENVIRO=${DEPLOYMENT_ENVIRO}
+HEALTHCHECK_PORT=${HEALTHCHECK_PORT}
+
+# Proxy configurations, the proxy values come for the environment,
+# which is important for the vmmoc
+HTTPS_PROXY=${HTTPS_PROXY}
+HTTP_PROXY=${HTTP_PROXY}
+NO_PROXY=${NO_PROXY}
+
+# Maven/Java specific configurations
+MAVEN_REPO_LOCAL=/home/nos3/.nos3/.m2
+MAVEN_HTTPS_PROXY="--settings ./settings.xml"
+MAVEN_SETTINGS_FILE=${MAVEN_SETTINGS_FILE}
+
+# 42 specific configurations
+FORTYTWO_DISPLAY=:1
+FORTYTWO_GIT_URL=https://github.com/nasa-itc/42.git
+FORTYTWO_GIT_COMMIT=nos3-main
+FORTYTWO_GIT_FOLDER=42
+FORTYTWO_STARTUP_FOLDER=NOS3InOut
+FORTYTWO_RECOMPILE=false
+FORTYTWO_BASE_DIR=/opt/nasa-itc
+FORTYTWO_VNC_PASSWORD=$(echo -n "foobar" | shasum -a 256 | cut -d" " -f1)
+
+FORTYTWO_HOST=${FORTYTWO_HOST}
+FORTYTWO_PORT=${FORTYTWO_PORT}
+FORTYTWO_HOST_PORT=${FORTYTWO_HOST_PORT}
+
+FORTYTWO_SIM_DATE="$(date -ud "@${SIM_T0_EPOCH_SECONDS}" +"%m %d %Y")"
+FORTYTWO_SIM_TIME="$(date -ud "@${SIM_T0_EPOCH_SECONDS}" +"%H %M %S.%2N")"
+FORTYTWO_LEAP_SECONDS=${LEAP_SECONDS}
+
+# gsw+fsw specific configurations
+GSW_SOFTWARE=${GSW_SOFTWARE}
+FSW_SOFTWARE=${FSW_SOFTWARE}
+
+COMPONENT_DIR=/home/nos3/builds/nos3/components
+
+# yamcs specific configurations
+YAMCS_DATA_DIR=/home/nos3/.nos3/yamcs/target/yamcs/yamcs-data
+# YAMCS_ETC_DIR=/home/nos3/.nos3/yamcs/target/yamcs/yamcs-etc
+
+# YAMCS_CACHE_DIR=/home/nos3/.nos3/yamcs/target/yamcs/yam
+
+YAMCS_GIT_URL=https://github.com/nasa-itc/nos3_yamcs_master.git
+YAMCS_GIT_COMMIT=nos3-dev
+
+YAMCS_INSTANCES=nos3
+
+YAMCS_HOST=${YAMCS_HOST}
+YAMCS_HOST_PORT=${YAMCS_HOST_PORT}
+YAMCS_PORT=${YAMCS_PORT}
+
+# openmct specific configurations
+OPENMCT_IMAGE_URI=docker.io/library/ubuntu:25.04
+
+OPENMCT_GIT_URL=https://github.com/akhenry/openmct-yamcs.git
+OPENMCT_GIT_COMMIT=master
+
+OPENMCT_NVM_VERSION=v0.40.1
+OPENMCT_NODE_VERSION=v21.7.3
+
+OPENMCT_HOST=${OPENMCT_HOST}
+OPENMCT_PORT=${OPENMCT_PORT}
+OPENMCT_HOST_PORT=${OPENMCT_HOST_PORT}
+
+# nasa-itc specific configurations
+BASE_DIR=/opt/nasa-itc
+
+EOF
\ No newline at end of file