feat: add submitted skills review desk
This commit is contained in:
@@ -0,0 +1,277 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build a NDO service locally with Docker, push to artifactory, point a k8s deployment at it.
|
||||
set -euo pipefail
|
||||
|
||||
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# shellcheck source=lib/env.sh
|
||||
source "$HERE/lib/env.sh"
|
||||
|
||||
REG="${NDO_REGISTRY:-artifactorycn.netcracker.com:17009}"
|
||||
ART_USER="${NDO_ARTIFACTORY_USER:-$USER}"
|
||||
PLATFORM="${NDO_PLATFORM:-linux/amd64}"
|
||||
PROJECTS="${NDO_PROJECTS:-$HOME/projects}"
|
||||
|
||||
ENV_ALIAS="${NDO_ENV:-}"
|
||||
SVC=""; DIR=""; TAG=""; TICKET=""; DFILE=""; TARGET="release"
|
||||
YES=0; NOCACHE=0; SKIP_TESTS=0; TIMEOUT="10m"
|
||||
|
||||
die() { echo "ERROR: $*" >&2; exit 1; }
|
||||
say() { echo "==> $*" >&2; }
|
||||
|
||||
usage() {
|
||||
cat <<'USAGE'
|
||||
ndo-ship.sh — local build → artifactory → k8s deploy for NDO services.
|
||||
|
||||
Commands:
|
||||
doctor check docker/buildx/registry-login/kubectl
|
||||
login docker login to artifactory (interactive)
|
||||
tag <service> print the image ref that would be built
|
||||
test <service> run unit tests only (maven, or docker --target test)
|
||||
build <service> build the image (runs unit tests first unless --skip-tests)
|
||||
push <service> push the last built (or --tag'd) image
|
||||
deploy <service> -e ENV point <service>-v1 at the image + wait for rollout [needs --yes]
|
||||
ship <service> -e ENV test → build → push → deploy → rollout wait [needs --yes]
|
||||
status <service> -e ENV deployed image, replicas, pod state
|
||||
rollback <service> -e ENV restore the image recorded before the last deploy [needs --yes]
|
||||
pullsecret <service> -e ENV attach local docker creds as an imagePullSecret (ImagePullBackOff fix) [needs --yes]
|
||||
|
||||
Options:
|
||||
-e, --env ALIAS target env (see: ndo-api.sh env ls). Ambiguous short names are rejected.
|
||||
-t, --tag TAG image tag (default: UTC timestamp, always unique)
|
||||
--ticket N UNM number for the repo name (default: parsed from git branch)
|
||||
-d, --dir PATH service repo (default: $NDO_PROJECTS/<service>)
|
||||
-f, --file FILE dockerfile (default: Dockerfile_local, falls back to Dockerfile)
|
||||
--target STAGE build target (default: release; ignored if the dockerfile has no stages)
|
||||
--platform P default linux/amd64 — do NOT drop this on an arm64 Mac
|
||||
--skip-tests skip unit tests in build/ship
|
||||
--no-cache docker build --no-cache
|
||||
--timeout D rollout wait (default 10m)
|
||||
-y, --yes confirm a cluster-mutating command (deploy/ship/rollback/pullsecret)
|
||||
|
||||
Image ref: $REG/<artifactory-user>/<service>_unm_<ticket>:<tag>
|
||||
Env overrides: NDO_REGISTRY NDO_ARTIFACTORY_USER NDO_PLATFORM NDO_PROJECTS NDO_ENV
|
||||
USAGE
|
||||
}
|
||||
|
||||
parse_opts() {
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
-e|--env) ENV_ALIAS="$2"; shift 2 ;;
|
||||
-t|--tag) TAG="$2"; shift 2 ;;
|
||||
--ticket) TICKET="$2"; shift 2 ;;
|
||||
-d|--dir) DIR="$2"; shift 2 ;;
|
||||
-f|--file) DFILE="$2"; shift 2 ;;
|
||||
--target) TARGET="$2"; shift 2 ;;
|
||||
--platform) PLATFORM="$2"; shift 2 ;;
|
||||
--timeout) TIMEOUT="$2"; shift 2 ;;
|
||||
--skip-tests) SKIP_TESTS=1; shift ;;
|
||||
--no-cache) NOCACHE=1; shift ;;
|
||||
-y|--yes) YES=1; shift ;;
|
||||
-*) die "unknown option $1" ;;
|
||||
*) [ -z "$SVC" ] && SVC="$1" || die "unexpected arg $1"; shift ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
need_svc() { [ -n "$SVC" ] || die "no service given"; }
|
||||
|
||||
svc_dir() {
|
||||
need_svc
|
||||
[ -n "$DIR" ] || DIR="$PROJECTS/$SVC"
|
||||
[ -d "$DIR" ] || die "service repo not found: $DIR (use --dir)"
|
||||
echo "$DIR"
|
||||
}
|
||||
|
||||
dockerfile() {
|
||||
local d; d="$(svc_dir)"
|
||||
if [ -n "$DFILE" ]; then [ -f "$d/$DFILE" ] || [ -f "$DFILE" ] || die "dockerfile not found: $DFILE"; echo "$DFILE"; return; fi
|
||||
if [ -f "$d/Dockerfile_local" ]; then echo "Dockerfile_local"; return; fi
|
||||
echo "Dockerfile"
|
||||
echo "no Dockerfile_local in $d — using Dockerfile. If the build pulls shared/external artifacts, create Dockerfile_local (see reference/dockerfile-local.md)." >&2
|
||||
}
|
||||
|
||||
ticket() {
|
||||
[ -n "$TICKET" ] && { echo "$TICKET"; return; }
|
||||
local d b; d="$(svc_dir)"
|
||||
b=$(git -C "$d" branch --show-current 2>/dev/null || true)
|
||||
if [[ "$b" =~ [Uu][Nn][Mm][-_]?([0-9]+) ]]; then echo "${BASH_REMATCH[1]}"; else echo "local"; fi
|
||||
}
|
||||
|
||||
image_ref() {
|
||||
need_svc
|
||||
local t; t="${TAG:-$(date -u +%Y%m%d-%H%M%S)}"
|
||||
echo "$REG/$ART_USER/${SVC}_unm_$(ticket):$t"
|
||||
}
|
||||
|
||||
last_image_file() { mkdir -p "$NDO_CACHE/last-image"; echo "$NDO_CACHE/last-image/$SVC"; }
|
||||
|
||||
resolve_image() {
|
||||
if [ -n "$TAG" ]; then image_ref; return; fi
|
||||
local f; f="$(last_image_file)"
|
||||
[ -s "$f" ] || die "no image built yet for $SVC — run 'build' first or pass --tag"
|
||||
cat "$f"
|
||||
}
|
||||
|
||||
confirm() {
|
||||
[ "$YES" -eq 1 ] || die "'$1' mutates shared env '$ENV_ALIAS' (context $NDO_CTX, ns $NDO_NS). Re-run with --yes once the user has approved."
|
||||
}
|
||||
|
||||
container_name() {
|
||||
local names first
|
||||
names=$(kubectl --context="$NDO_CTX" -n "$NDO_NS" get deploy "$SVC-v1" \
|
||||
-o jsonpath='{range .spec.template.spec.containers[*]}{.name}{"\n"}{end}')
|
||||
if grep -qx "$SVC" <<<"$names"; then echo "$SVC"; else first=$(head -1 <<<"$names"); [ -n "$first" ] || die "no containers in $SVC-v1"; echo "$first"; fi
|
||||
}
|
||||
|
||||
current_image() {
|
||||
kubectl --context="$NDO_CTX" -n "$NDO_NS" get deploy "$SVC-v1" \
|
||||
-o jsonpath='{.spec.template.spec.containers[0].image}'
|
||||
}
|
||||
|
||||
rollback_file() { mkdir -p "$NDO_CACHE/rollback"; echo "$NDO_CACHE/rollback/$(tr '/' '_' <<<"$ENV_ALIAS")__$SVC"; }
|
||||
|
||||
is_maven() { [ -f "$(svc_dir)/pom.xml" ]; }
|
||||
is_go() { [ -f "$(svc_dir)/go.mod" ]; }
|
||||
has_stages() { grep -qiE '^[[:space:]]*FROM .* AS ' "$(svc_dir)/$(dockerfile)"; }
|
||||
copies_target() { grep -qE 'COPY .*target/' "$(svc_dir)/$(dockerfile)"; }
|
||||
|
||||
mvn_env() {
|
||||
export JAVA_HOME="${JAVA_HOME:-/Library/Java/JavaVirtualMachines/jdk-25.0.2.jdk/Contents/Home}"
|
||||
export PATH="$JAVA_HOME/bin:$PATH"
|
||||
}
|
||||
|
||||
run_tests() {
|
||||
local d; d="$(svc_dir)"
|
||||
if is_maven; then
|
||||
say "maven unit tests ($SVC)"
|
||||
( mvn_env; cd "$d" && mvn -B test )
|
||||
elif is_go && grep -qiE '^[[:space:]]*FROM .* AS test' "$d/$(dockerfile)"; then
|
||||
say "docker test stage ($SVC)"
|
||||
docker build --platform "$PLATFORM" -f "$d/$(dockerfile)" --target test -t "$SVC-test:local" "$d"
|
||||
elif is_go; then
|
||||
say "go test ($SVC)"
|
||||
( cd "$d" && go test ./... )
|
||||
else
|
||||
say "no unit-test runner detected for $SVC — skipping"
|
||||
fi
|
||||
}
|
||||
|
||||
do_build() {
|
||||
local d df img args=()
|
||||
d="$(svc_dir)"; df="$(dockerfile)"; img="$(image_ref)"
|
||||
[ "$SKIP_TESTS" -eq 1 ] || run_tests
|
||||
# Java services copy target/*.jar into the image — package first.
|
||||
if is_maven && copies_target; then
|
||||
say "mvn package -DskipTests (jar for the image layer)"
|
||||
( mvn_env; cd "$d" && mvn -B -DskipTests package )
|
||||
fi
|
||||
args=(build --platform "$PLATFORM" -f "$d/$df" -t "$img")
|
||||
has_stages && grep -qiE "^[[:space:]]*FROM .* AS $TARGET\$" "$d/$df" && args+=(--target "$TARGET")
|
||||
[ "$NOCACHE" -eq 1 ] && args+=(--no-cache)
|
||||
args+=("$d")
|
||||
say "docker ${args[*]}"
|
||||
docker "${args[@]}"
|
||||
echo "$img" > "$(last_image_file)"
|
||||
echo "$img"
|
||||
}
|
||||
|
||||
do_push() {
|
||||
local img; img="$(resolve_image)"
|
||||
say "docker push $img"
|
||||
docker push "$img"
|
||||
echo "$img"
|
||||
}
|
||||
|
||||
do_deploy() {
|
||||
local img c prev
|
||||
env_resolve "$ENV_ALIAS"
|
||||
confirm deploy
|
||||
img="$(resolve_image)"
|
||||
c="$(container_name)"
|
||||
prev="$(current_image)"
|
||||
echo "$prev" > "$(rollback_file)"
|
||||
say "rollback point saved: $prev"
|
||||
say "set image $SVC-v1/$c=$img (ctx=$NDO_CTX ns=$NDO_NS)"
|
||||
kubectl --context="$NDO_CTX" -n "$NDO_NS" set image "deploy/$SVC-v1" "$c=$img"
|
||||
kubectl --context="$NDO_CTX" -n "$NDO_NS" rollout status "deploy/$SVC-v1" --timeout="$TIMEOUT" || {
|
||||
echo "--- rollout failed; pod events ---" >&2
|
||||
kubectl --context="$NDO_CTX" -n "$NDO_NS" get pod -l "app=$SVC-v1" \
|
||||
-o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.phase}{"\t"}{range .status.containerStatuses[*]}{.state}{end}{"\n"}{end}' >&2
|
||||
echo "ImagePullBackOff => node has no creds for $REG. Fix: ndo-ship.sh pullsecret $SVC -e $ENV_ALIAS --yes" >&2
|
||||
return 1
|
||||
}
|
||||
do_status
|
||||
}
|
||||
|
||||
do_status() {
|
||||
env_resolve "$ENV_ALIAS"
|
||||
need_svc
|
||||
echo "env : $ENV_ALIAS (ctx=$NDO_CTX ns=$NDO_NS)"
|
||||
echo "image : $(current_image)"
|
||||
kubectl --context="$NDO_CTX" -n "$NDO_NS" get deploy "$SVC-v1" \
|
||||
-o custom-columns='READY:.status.readyReplicas,DESIRED:.spec.replicas,UPDATED:.status.updatedReplicas'
|
||||
kubectl --context="$NDO_CTX" -n "$NDO_NS" get pod -l "app=$SVC-v1" \
|
||||
-o custom-columns='POD:.metadata.name,PHASE:.status.phase,RESTARTS:.status.containerStatuses[0].restartCount,AGE:.metadata.creationTimestamp'
|
||||
}
|
||||
|
||||
do_rollback() {
|
||||
local f prev c
|
||||
env_resolve "$ENV_ALIAS"
|
||||
confirm rollback
|
||||
f="$(rollback_file)"
|
||||
[ -s "$f" ] || die "no rollback point recorded for $SVC on $ENV_ALIAS"
|
||||
prev="$(cat "$f")"; c="$(container_name)"
|
||||
say "restoring $prev"
|
||||
kubectl --context="$NDO_CTX" -n "$NDO_NS" set image "deploy/$SVC-v1" "$c=$prev"
|
||||
kubectl --context="$NDO_CTX" -n "$NDO_NS" rollout status "deploy/$SVC-v1" --timeout="$TIMEOUT"
|
||||
}
|
||||
|
||||
do_pullsecret() {
|
||||
env_resolve "$ENV_ALIAS"
|
||||
confirm pullsecret
|
||||
local sec=ndo-repro-artifactory pw
|
||||
pw=$(printf '%s' "$REG" | docker-credential-osxkeychain get 2>/dev/null \
|
||||
| python3 -c 'import sys,json;print(json.load(sys.stdin)["Secret"])') || die "no local docker creds for $REG — run: ndo-ship.sh login"
|
||||
kubectl --context="$NDO_CTX" -n "$NDO_NS" create secret docker-registry "$sec" \
|
||||
--docker-server="$REG" --docker-username="$ART_USER" --docker-password="$pw" \
|
||||
--dry-run=client -o yaml | kubectl --context="$NDO_CTX" -n "$NDO_NS" apply -f -
|
||||
unset pw
|
||||
kubectl --context="$NDO_CTX" -n "$NDO_NS" patch deploy "$SVC-v1" \
|
||||
-p "{\"spec\":{\"template\":{\"spec\":{\"imagePullSecrets\":[{\"name\":\"$sec\"}]}}}}"
|
||||
kubectl --context="$NDO_CTX" -n "$NDO_NS" rollout status "deploy/$SVC-v1" --timeout="$TIMEOUT"
|
||||
}
|
||||
|
||||
do_doctor() {
|
||||
printf 'docker : %s\n' "$(docker version --format '{{.Server.Version}}' 2>&1 | head -1)"
|
||||
printf 'context : %s\n' "$(docker context show 2>/dev/null)"
|
||||
printf 'buildx : %s\n' "$(docker buildx version 2>&1 | head -1)"
|
||||
printf 'host arch : %s (build platform %s)\n' "$(uname -m)" "$PLATFORM"
|
||||
if printf '%s' "$REG" | docker-credential-osxkeychain get >/dev/null 2>&1; then
|
||||
printf 'registry : logged in to %s as %s\n' "$REG" "$ART_USER"
|
||||
else
|
||||
printf 'registry : NOT logged in to %s — run: ndo-ship.sh login\n' "$REG"
|
||||
fi
|
||||
printf 'envs : %s\n' "$(awk -F'\t' '!/^#/&&NF>=4' "$(env_file)" | wc -l | tr -d ' ') registered"
|
||||
[ -n "$ENV_ALIAS" ] && { env_resolve "$ENV_ALIAS"; printf 'env %-10s: ctx=%s ns=%s\n gw=%s\n' "$ENV_ALIAS" "$NDO_CTX" "$NDO_NS" "$NDO_GW"; \
|
||||
kubectl --context="$NDO_CTX" -n "$NDO_NS" get deploy -o name >/dev/null 2>&1 \
|
||||
&& echo 'kube access : ok' || echo 'kube access : FAILED (VPN down or creds expired)'; }
|
||||
return 0
|
||||
}
|
||||
|
||||
CMD="${1:-}"; shift || true
|
||||
case "$CMD" in
|
||||
doctor) parse_opts "$@"; do_doctor ;;
|
||||
login) docker login "$REG" ;;
|
||||
tag) parse_opts "$@"; image_ref ;;
|
||||
test) parse_opts "$@"; run_tests ;;
|
||||
build) parse_opts "$@"; do_build ;;
|
||||
push) parse_opts "$@"; do_push ;;
|
||||
deploy) parse_opts "$@"; do_deploy ;;
|
||||
status) parse_opts "$@"; do_status ;;
|
||||
rollback) parse_opts "$@"; do_rollback ;;
|
||||
pullsecret) parse_opts "$@"; do_pullsecret ;;
|
||||
ship) parse_opts "$@"; env_resolve "$ENV_ALIAS"; confirm ship
|
||||
do_build >/dev/null; TAG=""; do_push >/dev/null; do_deploy ;;
|
||||
""|-h|--help|help) usage ;;
|
||||
*) die "unknown command: $CMD (see --help)" ;;
|
||||
esac
|
||||
Reference in New Issue
Block a user